From 7c05e7a356edfcee62e5736b281ddb7c06f11f39 Mon Sep 17 00:00:00 2001 From: KGSN Date: Fri, 7 Jan 2022 17:52:14 +0800 Subject: [PATCH 1/2] feat: cos - replication --- go.mod | 2 +- go.sum | 2 + tencentcloud/basic_test.go | 1 + tencentcloud/connectivity/client.go | 5 +- tencentcloud/resource_tc_cos_bucket.go | 169 +++++- tencentcloud/resource_tc_cos_bucket_test.go | 106 ++++ tencentcloud/service_tencentcloud_cos.go | 88 +++ .../tencentyun/cos-go-sdk-v5/.gitignore | 1 + .../tencentyun/cos-go-sdk-v5/CHANGELOG.md | 59 +- .../tencentyun/cos-go-sdk-v5/auth.go | 192 +++++- .../tencentyun/cos-go-sdk-v5/bucket.go | 11 + .../github.com/tencentyun/cos-go-sdk-v5/ci.go | 574 ++++++++++++++++-- .../tencentyun/cos-go-sdk-v5/ci_doc.go | 20 +- .../tencentyun/cos-go-sdk-v5/ci_media.go | 342 ++++++++++- .../tencentyun/cos-go-sdk-v5/cos.go | 47 +- .../tencentyun/cos-go-sdk-v5/error.go | 21 + .../tencentyun/cos-go-sdk-v5/helper.go | 15 +- .../tencentyun/cos-go-sdk-v5/object.go | 141 ++++- .../tencentyun/cos-go-sdk-v5/object_part.go | 17 +- vendor/modules.txt | 2 +- website/docs/r/cos_bucket.html.markdown | 10 + 21 files changed, 1704 insertions(+), 121 deletions(-) diff --git a/go.mod b/go.mod index 5775855102..93d4e76ec2 100644 --- a/go.mod +++ b/go.mod @@ -57,7 +57,7 @@ require ( github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod v1.0.199 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc v1.0.199 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/wss v1.0.199 - github.com/tencentyun/cos-go-sdk-v5 v0.7.31-0.20210902132439-360bc9b1be6b + github.com/tencentyun/cos-go-sdk-v5 v0.7.33 github.com/yangwenmai/ratelimit v0.0.0-20180104140304-44221c2292e1 github.com/zclconf/go-cty v1.4.2 // indirect golang.org/x/sys v0.0.0-20200523222454-059865788121 // indirect diff --git a/go.sum b/go.sum index a4c2619149..820231f3e8 100644 --- a/go.sum +++ b/go.sum @@ -536,6 +536,8 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/wss v1.0.199 h1:hMBLtiJ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/wss v1.0.199/go.mod h1:nnY91/H3j/Gu7V/oCA6Zeg8T5D3q36EUdBh4EjmHwqY= github.com/tencentyun/cos-go-sdk-v5 v0.7.31-0.20210902132439-360bc9b1be6b h1:rLl5sAeLt382023Kd3X4TaOEaT2hdgXWwTGyKiy16Zo= github.com/tencentyun/cos-go-sdk-v5 v0.7.31-0.20210902132439-360bc9b1be6b/go.mod h1:4E4+bQ2gBVJcgEC9Cufwylio4mXOct2iu05WjgEBx1o= +github.com/tencentyun/cos-go-sdk-v5 v0.7.33 h1:5jmJU7U/1nf/7ZPDkrUL8KlF1oDUzTHsdtLNY6x0hq4= +github.com/tencentyun/cos-go-sdk-v5 v0.7.33/go.mod h1:4E4+bQ2gBVJcgEC9Cufwylio4mXOct2iu05WjgEBx1o= github.com/tetafro/godot v0.3.7 h1:+mecr7RKrUKB5UQ1gwqEMn13sDKTyDR8KNIquB9mm+8= github.com/tetafro/godot v0.3.7/go.mod h1:/7NLHhv08H1+8DNj0MElpAACw1ajsCuf3TKNQxA5S+0= github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e h1:RumXZ56IrCj4CL+g1b9OL/oH0QnsF976bC8xQFYUD5Q= diff --git a/tencentcloud/basic_test.go b/tencentcloud/basic_test.go index dc03dae8c6..54690622b4 100644 --- a/tencentcloud/basic_test.go +++ b/tencentcloud/basic_test.go @@ -10,6 +10,7 @@ the following must be changed to your resource id. */ var appid string = os.Getenv("TENCENTCLOUD_APPID") +var ownerUin string = os.Getenv("TENCENTCLOUD_OWNER_UIN") const ( defaultRegion = "ap-guangzhou" diff --git a/tencentcloud/connectivity/client.go b/tencentcloud/connectivity/client.go index e3af52e5ee..e2a34892bb 100644 --- a/tencentcloud/connectivity/client.go +++ b/tencentcloud/connectivity/client.go @@ -152,11 +152,12 @@ func (me *TencentCloudClient) UseCosClient() *s3.S3 { // UseTencentCosClient tencent cloud own client for service instead of aws func (me *TencentCloudClient) UseTencentCosClient(bucket string) *cos.Client { - if me.tencentCosConn != nil { + u, _ := url.Parse(fmt.Sprintf("https://%s.cos.%s.myqcloud.com", bucket, me.Region)) + + if me.tencentCosConn != nil && me.tencentCosConn.BaseURL.BucketURL == u { return me.tencentCosConn } - u, _ := url.Parse(fmt.Sprintf("https://%s.cos.%s.myqcloud.com", bucket, me.Region)) baseUrl := &cos.BaseURL{ BucketURL: u, } diff --git a/tencentcloud/resource_tc_cos_bucket.go b/tencentcloud/resource_tc_cos_bucket.go index 88cd47ab50..1064518b28 100644 --- a/tencentcloud/resource_tc_cos_bucket.go +++ b/tencentcloud/resource_tc_cos_bucket.go @@ -385,6 +385,47 @@ func resourceTencentCloudCosBucket() *schema.Resource { Default: false, Description: "Enable bucket versioning.", }, + "replica_role": { + Type: schema.TypeString, + Optional: true, + RequiredWith: []string{"replica_rules", "versioning_enable"}, + Description: "Request initiator identifier, format: `qcs::cam::uin/:uin/.`. NOTE: only `versioning_enable` is true can configure this argument.", + }, + "replica_rules": { + Type: schema.TypeList, + Optional: true, + Description: "List of replica rule. NOTE: only `versioning_enable` is true and `replica_role` set can configure this argument.", + RequiredWith: []string{"replica_role", "versioning_enable"}, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Optional: true, + Description: "Name of a specific rule.", + }, + "status": { + Type: schema.TypeString, + Required: true, + Description: "Status identifier, available values: `Enabled`, `Disabled`.", + }, + "prefix": { + Type: schema.TypeString, + Optional: true, + Description: "Prefix matching policy. Policies cannot overlap; otherwise, an error will be returned. To match the root directory, leave this parameter empty.", + }, + "destination_bucket": { + Type: schema.TypeString, + Required: true, + Description: "Destination bucket identifier, format: `qcs::cos:::`. NOTE: destination bucket must enable versioning.", + }, + "destination_storage_class": { + Type: schema.TypeString, + Optional: true, + Description: "Storage class of destination, available values: `STANDARD`, `INTELLIGENT_TIERING`, `STANDARD_IA`. default is following current class of destination.", + }, + }, + }, + }, "cors_rules": { Type: schema.TypeList, Optional: true, @@ -569,18 +610,18 @@ func resourceTencentCloudCosBucket() *schema.Resource { Computed: true, 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`.", }, - //computed - "cos_bucket_url": { - Type: schema.TypeString, - Computed: true, - Description: "The URL of this cos bucket.", - }, "multi_az": { Type: schema.TypeBool, Optional: true, ForceNew: true, Description: "Indicates whether to create a bucket of multi available zone.", }, + //computed + "cos_bucket_url": { + Type: schema.TypeString, + Computed: true, + Description: "The URL of this cos bucket.", + }, }, } } @@ -595,6 +636,17 @@ func resourceTencentCloudCosBucketCreate(d *schema.ResourceData, meta interface{ bucket := d.Get("bucket").(string) acl := d.Get("acl").(string) + role, roleOk := d.GetOk("replica_role") + rule, ruleOk := d.GetOk("replica_rules") + versioning, versioningOk := d.GetOk("versioning_enable") + + if v := versioning.(bool); !versioningOk || !v { + if roleOk || role.(string) != "" { + return fmt.Errorf("cannot configure role unless versioning enable") + } else if ruleOk || len(rule.([]interface{})) > 0 { + return fmt.Errorf("cannot configure replica rule unless versioning enable") + } + } cosService := CosService{client: meta.(*TencentCloudClient).apiV3Conn} @@ -713,6 +765,18 @@ func resourceTencentCloudCosBucketRead(d *schema.ResourceData, meta interface{}) return fmt.Errorf("setting versioning_enable error: %v", err) } + replicaResult, err := cosService.GetBucketReplication(ctx, bucket) + if err != nil { + return err + } + + if replicaResult != nil { + err := setBucketReplication(d, *replicaResult) + if err != nil { + return err + } + } + //read the log logEnable, logTargetBucket, logPrefix, err := cosService.GetBucketLogStatus(ctx, bucket) if err != nil { @@ -823,6 +887,14 @@ func resourceTencentCloudCosBucketUpdate(d *schema.ResourceData, meta interface{ d.SetPartial("versioning_enable") } + if d.HasChange("replica_role") || d.HasChange("replica_rules") { + err := resourceTencentCloudCosBucketReplicaUpdate(ctx, cosService, d) + + if err != nil { + return err + } + } + if d.HasChange("tags") { bucket := d.Id() @@ -949,6 +1021,40 @@ func resourceTencentCloudCosBucketVersioningUpdate(ctx context.Context, client * return nil } +func resourceTencentCloudCosBucketReplicaUpdate(ctx context.Context, service CosService, d *schema.ResourceData) error { + bucket := d.Get("bucket").(string) + oldRole, newRole := d.GetChange("replica_role") + oldRules, newRules := d.GetChange("replica_rules") + oldRuleLength := len(oldRules.([]interface{})) + newRuleLength := len(newRules.([]interface{})) + + // check if remove + if oldRole.(string) != "" && newRole.(string) == "" || oldRuleLength > 0 && newRuleLength == 0 { + result, err := service.GetBucketReplication(ctx, bucket) + if err != nil { + return err + } + + if result != nil { + err := service.DeleteBucketReplication(ctx, d.Get("bucket").(string)) + if err != nil { + return err + } + } + } else if newRole.(string) != "" || newRuleLength > 0 { + role, rules, _ := getBucketReplications(d) + err := service.PutBucketReplication(ctx, d.Get("bucket").(string), role, rules) + if err != nil { + return err + } + } + + d.SetPartial("replica_role") + d.SetPartial("replica_rules") + + return nil +} + func resourceTencentCloudCosBucketAclUpdate(ctx context.Context, client *s3.S3, d *schema.ResourceData) error { logId := getLogId(ctx) @@ -1471,3 +1577,54 @@ func transitionHash(v interface{}) int { } return hashcode.String(buf.String()) } + +func getBucketReplications(d *schema.ResourceData) (role string, rules []cos.BucketReplicationRule, err error) { + role = d.Get("replica_role").(string) + replicaRules := d.Get("replica_rules").([]interface{}) + for i := range replicaRules { + item := replicaRules[i].(map[string]interface{}) + rule := cos.BucketReplicationRule{ + Status: item["status"].(string), + Destination: &cos.ReplicationDestination{ + Bucket: item["destination_bucket"].(string), + }, + } + if v, ok := item["prefix"].(string); ok { + rule.Prefix = v + } + if v, ok := item["id"].(string); ok { + rule.ID = v + } + if v, ok := item["destination_storage_class"].(string); ok { + rule.Destination.StorageClass = v + } + rules = append(rules, rule) + } + return +} + +func setBucketReplication(d *schema.ResourceData, result cos.GetBucketReplicationResult) (err error) { + if result.Role != "" { + err = d.Set("replica_role", result.Role) + } + rules := make([]map[string]interface{}, 0) + if len(result.Rule) > 0 { + for i := range result.Rule { + item := result.Rule[i] + rule := map[string]interface{}{ + "status": item.Status, + "destination_bucket": item.Destination.Bucket, + "destination_storage_class": item.Destination.StorageClass, + } + if item.ID != "" { + rule["id"] = item.ID + } + if item.Prefix != "" { + rule["prefix"] = item.Prefix + } + rules = append(rules, rule) + } + } + err = d.Set("replica_rules", rules) + return +} diff --git a/tencentcloud/resource_tc_cos_bucket_test.go b/tencentcloud/resource_tc_cos_bucket_test.go index 908b91e8a0..dc14041392 100644 --- a/tencentcloud/resource_tc_cos_bucket_test.go +++ b/tencentcloud/resource_tc_cos_bucket_test.go @@ -375,6 +375,51 @@ func TestAccTencentCloudCosBucket_originDomain(t *testing.T) { }) } +func TestAccTencentCloudCosBucket_replication(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckCosBucketDestroy, + Steps: []resource.TestStep{ + { + Config: testAccBucketReplication(appid, ownerUin, ownerUin), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckCosBucketExists("tencentcloud_cos_bucket.with_replication"), + resource.TestCheckResourceAttr("tencentcloud_cos_bucket.with_replication", "replica_role", fmt.Sprintf("qcs::cam::uin/%s:uin/%s", ownerUin, ownerUin)), + resource.TestCheckResourceAttr("tencentcloud_cos_bucket.with_replication", "replica_rules.#", "1"), + resource.TestCheckResourceAttr("tencentcloud_cos_bucket.with_replication", "replica_rules.0.id", "test-rep1"), + resource.TestCheckResourceAttr("tencentcloud_cos_bucket.with_replication", "replica_rules.0.status", "Enabled"), + ), + }, + { + Config: testAccBucketReplicationUpdate(appid, ownerUin, ownerUin), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckCosBucketExists("tencentcloud_cos_bucket.with_replication"), + resource.TestCheckResourceAttr("tencentcloud_cos_bucket.with_replication", "replica_rules.#", "1"), + resource.TestCheckResourceAttr("tencentcloud_cos_bucket.with_replication", "replica_rules.0.status", "Disabled"), + resource.TestCheckResourceAttr("tencentcloud_cos_bucket.with_replication", "replica_rules.0.prefix", "dist"), + ), + }, + { + Config: testAccBucketReplicationRemove(appid), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckCosBucketExists("tencentcloud_cos_bucket.with_replication"), + resource.TestCheckResourceAttr("tencentcloud_cos_bucket.with_replication", "replica_role", ""), + resource.TestCheckResourceAttr("tencentcloud_cos_bucket.with_replication", "replica_rules.#", "0"), + ), + }, + { + ResourceName: "tencentcloud_cos_bucket.with_replication", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"acl", "replica_role"}, + }, + }, + }) +} + func testAccCheckCosBucketExists(n string) resource.TestCheckFunc { return func(s *terraform.State) error { logId := getLogId(contextNil) @@ -658,3 +703,64 @@ resource "tencentcloud_cos_bucket" "with_domain" { } `, appid) } + +func testAccBucketReplication(appid, ownerUin, subUin string) string { + return fmt.Sprintf(` +resource "tencentcloud_cos_bucket" "replica1" { + bucket = "tf-replica-foo-%s" + acl = "private" + versioning_enable = true +} + +resource "tencentcloud_cos_bucket" "with_replication" { + bucket = "tf-bucket-replica-%s" + acl = "private" + versioning_enable = true + replica_role = "qcs::cam::uin/%s:uin/%s" + replica_rules { + id = "test-rep1" + status = "Enabled" + destination_bucket = "qcs::cos:%s::${tencentcloud_cos_bucket.replica1.bucket}" + } +} +`, appid, appid, ownerUin, subUin, defaultRegion) +} + +func testAccBucketReplicationUpdate(appid, ownerUin, subUin string) string { + return fmt.Sprintf(` +resource "tencentcloud_cos_bucket" "replica1" { + bucket = "tf-replica-foo-%s" + acl = "private" + versioning_enable = true +} + +resource "tencentcloud_cos_bucket" "with_replication" { + bucket = "tf-bucket-replica-%s" + acl = "private" + versioning_enable = true + replica_role = "qcs::cam::uin/%s:uin/%s" + replica_rules { + id = "test-rep1" + status = "Disabled" + prefix = "dist" + destination_bucket = "qcs::cos:%s::${tencentcloud_cos_bucket.replica1.bucket}" + } +} +`, appid, appid, ownerUin, subUin, defaultRegion) +} + +func testAccBucketReplicationRemove(appid string) string { + return fmt.Sprintf(` +resource "tencentcloud_cos_bucket" "replica1" { + bucket = "tf-replica-foo-%s" + acl = "private" + versioning_enable = true +} + +resource "tencentcloud_cos_bucket" "with_replication" { + bucket = "tf-bucket-replica-%s" + acl = "private" + versioning_enable = true +} +`, appid, appid) +} diff --git a/tencentcloud/service_tencentcloud_cos.go b/tencentcloud/service_tencentcloud_cos.go index dc1071e845..6a3bcf020c 100644 --- a/tencentcloud/service_tencentcloud_cos.go +++ b/tencentcloud/service_tencentcloud_cos.go @@ -1084,3 +1084,91 @@ func (me *CosService) DeleteBucketOriginDomain(ctx context.Context, bucket strin return nil } + +func (me *CosService) GetBucketReplication(ctx context.Context, bucket string) (result *cos.GetBucketReplicationResult, errRet error) { + logId := getLogId(ctx) + defer func() { + if errRet != nil { + log.Printf("[CRITAL]%s api[%s] fail, reason[%s]\n", + logId, "GetBucketReplication", errRet.Error()) + } + }() + + ratelimit.Check("GetBucketReplication") + result, response, err := me.client.UseTencentCosClient(bucket).Bucket.GetBucketReplication(ctx) + + if response.StatusCode == 404 { + log.Printf("[WARN]%s, api[%s] returns %d", logId, "GetBucketReplication", response.StatusCode) + return + } + + resp, _ := json.Marshal(response) + + if err != nil { + errRet = err + return + } + + log.Printf("[DEBUG]%s api[%s] response body [%s]\n", + logId, "GetBucketReplication", resp) + + return +} + +func (me *CosService) PutBucketReplication(ctx context.Context, bucket string, role string, rules []cos.BucketReplicationRule) (errRet error) { + logId := getLogId(ctx) + + option := &cos.PutBucketReplicationOptions{ + Role: role, + Rule: rules, + } + + request, _ := xml.Marshal(option) + + defer func() { + if errRet != nil { + log.Printf("[CRITAL]%s api[%s] fail, request: %s reason[%s]\n", + logId, "PutBucketReplication", request, errRet.Error()) + } + }() + + ratelimit.Check("PutBucketReplication") + response, err := me.client.UseTencentCosClient(bucket).Bucket.PutBucketReplication(ctx, option) + + resp, _ := json.Marshal(response) + + if err != nil { + errRet = err + return + } + + log.Printf("[DEBUG]%s api[%s] response body [%s]\n", + logId, "PutBucketReplication", resp) + + return +} + +func (me *CosService) DeleteBucketReplication(ctx context.Context, bucket string) (errRet error) { + logId := getLogId(ctx) + defer func() { + if errRet != nil { + log.Printf("[CRITAL]%s api[%s] fail, reason[%s]\n", + logId, "DeleteBucketReplication", errRet.Error()) + } + }() + + ratelimit.Check("DeleteBucketReplication") + response, err := me.client.UseTencentCosClient(bucket).Bucket.DeleteBucketReplication(ctx) + + resp, _ := json.Marshal(response) + + if err != nil { + errRet = err + return + } + + log.Printf("[DEBUG]%s api[%s] response body [%s]\n", + logId, "DeleteBucketReplication", resp) + + return +} diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/.gitignore b/vendor/github.com/tencentyun/cos-go-sdk-v5/.gitignore index 8d1693a3d3..4dac9e1870 100644 --- a/vendor/github.com/tencentyun/cos-go-sdk-v5/.gitignore +++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/.gitignore @@ -29,3 +29,4 @@ cover.html cover.out covprofile coverage.html +example/CI/media_process/media_process diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/CHANGELOG.md b/vendor/github.com/tencentyun/cos-go-sdk-v5/CHANGELOG.md index 0660eb2417..32235f06e5 100644 --- a/vendor/github.com/tencentyun/cos-go-sdk-v5/CHANGELOG.md +++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/CHANGELOG.md @@ -7,9 +7,64 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). -## [v0.7.30](https://github.com/tencentyun/cos-go-sdk-v5/compare/v0.7.29...v0.7.30) - 2021-08-25 +## [v0.7.32](https://github.com/tencentyun/cos-go-sdk-v5/compare/v0.7.31...v0.7.32) - 2021-10-28 + +1、add ci interface +2、add ut + +### Merged + +- add ut [`#160`](https://github.com/tencentyun/cos-go-sdk-v5/pull/160) +- add CI 媒体处理接口 [`#159`](https://github.com/tencentyun/cos-go-sdk-v5/pull/159) +- Cos v4 dev [`#158`](https://github.com/tencentyun/cos-go-sdk-v5/pull/158) +- update auth signed header [`#157`](https://github.com/tencentyun/cos-go-sdk-v5/pull/157) +- Cos v4 dev [`#156`](https://github.com/tencentyun/cos-go-sdk-v5/pull/156) +- update PresignedURL [`#155`](https://github.com/tencentyun/cos-go-sdk-v5/pull/155) +- Cos v4 dev [`#154`](https://github.com/tencentyun/cos-go-sdk-v5/pull/154) +- Cos v4 dev [`#153`](https://github.com/tencentyun/cos-go-sdk-v5/pull/153) +- Cos v4 dev [`#152`](https://github.com/tencentyun/cos-go-sdk-v5/pull/152) +- Cos v4 dev [`#151`](https://github.com/tencentyun/cos-go-sdk-v5/pull/151) +- add:增加截图任务参数 [`#150`](https://github.com/tencentyun/cos-go-sdk-v5/pull/150) + +### Commits + +- add fetch task api && add cvm role [`d2e9d91`](https://github.com/tencentyun/cos-go-sdk-v5/commit/d2e9d919bdd6dba423ac39f0aa2d882ff85a4963) +- 内容审核六期接口 [`87f4943`](https://github.com/tencentyun/cos-go-sdk-v5/commit/87f49432d0514158982bdccda4a15c6504c98701) +- add retry options [`b0532f5`](https://github.com/tencentyun/cos-go-sdk-v5/commit/b0532f56c62b132897e0a3b333cbc43f1f5393da) +- add:解析回调body [`52bf0c7`](https://github.com/tencentyun/cos-go-sdk-v5/commit/52bf0c72b54bfad999edd9a542c9b44af61a2b58) +- add:支持多任务接口 [`84a85a1`](https://github.com/tencentyun/cos-go-sdk-v5/commit/84a85a159760405704c61e1085e790f378a3bf24) +- add:支持雪碧图任务 [`1f98f78`](https://github.com/tencentyun/cos-go-sdk-v5/commit/1f98f7857cd2cc5b70dbb1ae60628f4310c0c5c3) +- ci 分段任务struct demo提交 [`6b85e7b`](https://github.com/tencentyun/cos-go-sdk-v5/commit/6b85e7b9e72f223fab3eb9d5b1eea4b61bf7740c) +- add:支持动图任务 [`96b7f6d`](https://github.com/tencentyun/cos-go-sdk-v5/commit/96b7f6d79015704a4cae2bcf153eb601b290cf0c) +- fix:调整指针数组为实例十足 [`54fa46a`](https://github.com/tencentyun/cos-go-sdk-v5/commit/54fa46a83631caba6570d9276fe287ea4420d6bf) +- update ci doc [`48d7d86`](https://github.com/tencentyun/cos-go-sdk-v5/commit/48d7d868fe7dec6040a597a07ca3babef41e83b9) +- update [`d7b154b`](https://github.com/tencentyun/cos-go-sdk-v5/commit/d7b154bab1506ffa2b7ddbd834b64bfbc082a6a2) +- add:xml可见性 [`0263d6a`](https://github.com/tencentyun/cos-go-sdk-v5/commit/0263d6a6394a80c87a7c1c1507e048bdeffb15d5) +- update demo [`e9c0af3`](https://github.com/tencentyun/cos-go-sdk-v5/commit/e9c0af3ad1eb0582e9517c4f80af922e0e181469) +- update demo [`6372b2e`](https://github.com/tencentyun/cos-go-sdk-v5/commit/6372b2ef3cbc66be72e77e222e3d49c0d3ced254) +- fix:截图时输出文件是多个 [`134713e`](https://github.com/tencentyun/cos-go-sdk-v5/commit/134713eac32327ff6b09aaa57f95ec53fca28221) + +## [v0.7.31](https://github.com/tencentyun/cos-go-sdk-v5/compare/v0.7.30...v0.7.31) - 2021-09-07 + +更新CI接口、更新高级接口 -add append and retry +### Merged + +- 更新ci内容审核接口 [`#149`](https://github.com/tencentyun/cos-go-sdk-v5/pull/149) +- update upload/download opt [`#148`](https://github.com/tencentyun/cos-go-sdk-v5/pull/148) +- Cos v4 dev [`#146`](https://github.com/tencentyun/cos-go-sdk-v5/pull/146) +- Cos v4 dev [`#145`](https://github.com/tencentyun/cos-go-sdk-v5/pull/145) + +### Commits + +- update download retry && bucket domain [`a63f617`](https://github.com/tencentyun/cos-go-sdk-v5/commit/a63f6174b609f6b593cc274566b50aa5174705e6) +- add:支持拼接任务 [`ee4f335`](https://github.com/tencentyun/cos-go-sdk-v5/commit/ee4f335957f295497adbcf81a8e34bf38ba5e106) +- add:格式话 [`81d6170`](https://github.com/tencentyun/cos-go-sdk-v5/commit/81d6170a01b6bb50140cd738e7b91e812c2e641e) +- Updated CHANGELOG.md [`4c0eb2d`](https://github.com/tencentyun/cos-go-sdk-v5/commit/4c0eb2d7b78c5d58c7a9495111fefb8bf854b31a) +- update ci_media [`17cf1d4`](https://github.com/tencentyun/cos-go-sdk-v5/commit/17cf1d43003ba5998f23381437df03fa98a27f17) +- update upload/download [`fd0f5ed`](https://github.com/tencentyun/cos-go-sdk-v5/commit/fd0f5ed75aa97ed7719bbfd0da5913a825621094) + +## [v0.7.30](https://github.com/tencentyun/cos-go-sdk-v5/compare/v0.7.29...v0.7.30) - 2021-08-25 ### Merged diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/auth.go b/vendor/github.com/tencentyun/cos-go-sdk-v5/auth.go index 61f2b7b55f..abdf5169ef 100644 --- a/vendor/github.com/tencentyun/cos-go-sdk-v5/auth.go +++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/auth.go @@ -3,8 +3,10 @@ package cos import ( "crypto/hmac" "crypto/sha1" + "encoding/json" "fmt" "hash" + "io/ioutil" "net/http" "net/url" "sort" @@ -13,12 +15,21 @@ import ( "time" ) -const sha1SignAlgorithm = "sha1" -const privateHeaderPrefix = "x-cos-" -const defaultAuthExpire = time.Hour +const ( + sha1SignAlgorithm = "sha1" + privateHeaderPrefix = "x-cos-" + defaultAuthExpire = time.Hour +) + +var ( + defaultCVMAuthExpire = int64(600) + defaultCVMSchema = "http" + defaultCVMMetaHost = "metadata.tencentyun.com" + defaultCVMCredURI = "latest/meta-data/cam/security-credentials" +) // 需要校验的 Headers 列表 -var needSignHeaders = map[string]bool{ +var NeedSignHeaders = map[string]bool{ "host": true, "range": true, "x-cos-acl": true, @@ -37,21 +48,25 @@ var needSignHeaders = map[string]bool{ "content-type": true, "content-length": true, "content-md5": true, + "transfer-encoding": true, + "versionid": true, "expect": true, "expires": true, "x-cos-content-sha1": true, "x-cos-storage-class": true, + "if-match": true, "if-modified-since": true, + "if-none-match": true, + "if-unmodified-since": true, "origin": true, "access-control-request-method": true, "access-control-request-headers": true, "x-cos-object-type": true, } -var ciParameters = map[string]bool{ - "imagemogr2/": true, - "watermark/": true, - "imageview2/": true, +// 非线程安全,只能在进程初始化(而不是Client初始化)时做设置 +func SetNeedSignHeaders(key string, val bool) { + NeedSignHeaders[key] = val } func safeURLEncode(s string) string { @@ -67,7 +82,7 @@ func safeURLEncode(s string) string { type valuesSignMap map[string][]string func (vs valuesSignMap) Add(key, value string) { - key = strings.ToLower(key) + key = strings.ToLower(safeURLEncode(key)) vs[key] = append(vs[key], value) } @@ -85,7 +100,7 @@ func (vs valuesSignMap) Encode() string { for _, val := range items { pairs = append( pairs, - fmt.Sprintf("%s=%s", safeURLEncode(k), safeURLEncode(val))) + fmt.Sprintf("%s=%s", k, safeURLEncode(val))) } } return strings.Join(pairs, "&") @@ -126,12 +141,14 @@ func (a *AuthTime) keyString() string { } // newAuthorization 通过一系列步骤生成最终需要的 Authorization 字符串 -func newAuthorization(secretID, secretKey string, req *http.Request, authTime *AuthTime) string { +func newAuthorization(secretID, secretKey string, req *http.Request, authTime *AuthTime, signHost bool) string { signTime := authTime.signString() keyTime := authTime.keyString() signKey := calSignKey(secretKey, keyTime) - req.Header.Set("Host", req.Host) + if signHost { + req.Header.Set("Host", req.Host) + } formatHeaders := *new(string) signedHeaderList := *new([]string) formatHeaders, signedHeaderList = genFormatHeaders(req.Header) @@ -154,7 +171,7 @@ func AddAuthorizationHeader(secretID, secretKey string, sessionToken string, req } auth := newAuthorization(secretID, secretKey, req, - authTime, + authTime, true, ) if len(sessionToken) > 0 { req.Header.Set("x-cos-security-token", sessionToken) @@ -209,12 +226,9 @@ func genFormatString(method string, uri url.URL, formatParameters, formatHeaders func genFormatParameters(parameters url.Values) (formatParameters string, signedParameterList []string) { ps := valuesSignMap{} for key, values := range parameters { - key = strings.ToLower(key) for _, value := range values { - if !isCIParameter(key) { - ps.Add(key, value) - signedParameterList = append(signedParameterList, key) - } + ps.Add(key, value) + signedParameterList = append(signedParameterList, strings.ToLower(safeURLEncode(key))) } } //formatParameters = strings.ToLower(ps.Encode()) @@ -227,11 +241,10 @@ func genFormatParameters(parameters url.Values) (formatParameters string, signed func genFormatHeaders(headers http.Header) (formatHeaders string, signedHeaderList []string) { hs := valuesSignMap{} for key, values := range headers { - key = strings.ToLower(key) - for _, value := range values { - if isSignHeader(key) { + if isSignHeader(strings.ToLower(key)) { + for _, value := range values { hs.Add(key, value) - signedHeaderList = append(signedHeaderList, key) + signedHeaderList = append(signedHeaderList, strings.ToLower(safeURLEncode(key))) } } } @@ -254,17 +267,8 @@ func calHMACDigest(key, msg, signMethod string) []byte { return h.Sum(nil) } -func isCIParameter(key string) bool { - for k, v := range ciParameters { - if strings.HasPrefix(key, k) && v { - return true - } - } - return false -} - func isSignHeader(key string) bool { - for k, v := range needSignHeaders { + for k, v := range NeedSignHeaders { if key == k && v { return true } @@ -304,6 +308,13 @@ func (t *AuthorizationTransport) RoundTrip(req *http.Request) (*http.Response, e req = cloneRequest(req) // per RoundTrip contract ak, sk, token := t.GetCredential() + if strings.HasPrefix(ak, " ") || strings.HasSuffix(ak, " ") { + return nil, fmt.Errorf("SecretID is invalid") + } + if strings.HasPrefix(sk, " ") || strings.HasSuffix(sk, " ") { + return nil, fmt.Errorf("SecretKey is invalid") + } + // 增加 Authorization header authTime := NewAuthTime(defaultAuthExpire) AddAuthorizationHeader(ak, sk, token, req, authTime) @@ -318,3 +329,120 @@ func (t *AuthorizationTransport) transport() http.RoundTripper { } return http.DefaultTransport } + +type CVMSecurityCredentials struct { + TmpSecretId string `json:",omitempty"` + TmpSecretKey string `json:",omitempty"` + ExpiredTime int64 `json:",omitempty"` + Expiration string `json:",omitempty"` + Token string `json:",omitempty"` + Code string `json:",omitempty"` +} + +type CVMCredentialsTransport struct { + RoleName string + Transport http.RoundTripper + secretID string + secretKey string + sessionToken string + expiredTime int64 + rwLocker sync.RWMutex +} + +func (t *CVMCredentialsTransport) GetRoles() ([]string, error) { + urlname := fmt.Sprintf("%s://%s/%s", defaultCVMSchema, defaultCVMMetaHost, defaultCVMCredURI) + resp, err := http.Get(urlname) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode < 200 || resp.StatusCode > 299 { + bs, _ := ioutil.ReadAll(resp.Body) + return nil, fmt.Errorf("get cvm security-credentials role failed, StatusCode: %v, Body: %v", resp.StatusCode, string(bs)) + } + bs, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + roles := strings.Split(strings.TrimSpace(string(bs)), "\n") + if len(roles) == 0 { + return nil, fmt.Errorf("get cvm security-credentials role failed, No valid cam role was found") + } + return roles, nil +} + +// https://cloud.tencent.com/document/product/213/4934 +func (t *CVMCredentialsTransport) UpdateCredential(now int64) (string, string, string, error) { + t.rwLocker.Lock() + defer t.rwLocker.Unlock() + if t.expiredTime > now+defaultCVMAuthExpire { + return t.secretID, t.secretKey, t.sessionToken, nil + } + roleName := t.RoleName + if roleName == "" { + roles, err := t.GetRoles() + if err != nil { + return t.secretID, t.secretKey, t.sessionToken, err + } + roleName = roles[0] + } + urlname := fmt.Sprintf("%s://%s/%s/%s", defaultCVMSchema, defaultCVMMetaHost, defaultCVMCredURI, roleName) + resp, err := http.Get(urlname) + if err != nil { + return t.secretID, t.secretKey, t.sessionToken, err + } + defer resp.Body.Close() + if resp.StatusCode < 200 || resp.StatusCode > 299 { + bs, _ := ioutil.ReadAll(resp.Body) + return t.secretID, t.secretKey, t.sessionToken, fmt.Errorf("call cvm security-credentials failed, StatusCode: %v, Body: %v", resp.StatusCode, string(bs)) + } + var cred CVMSecurityCredentials + err = json.NewDecoder(resp.Body).Decode(&cred) + if err != nil { + return t.secretID, t.secretKey, t.sessionToken, err + } + if cred.Code != "Success" { + return t.secretID, t.secretKey, t.sessionToken, fmt.Errorf("call cvm security-credentials failed, Code:%v", cred.Code) + } + t.secretID, t.secretKey, t.sessionToken, t.expiredTime = cred.TmpSecretId, cred.TmpSecretKey, cred.Token, cred.ExpiredTime + return t.secretID, t.secretKey, t.sessionToken, nil +} + +func (t *CVMCredentialsTransport) GetCredential() (string, string, string, error) { + now := time.Now().Unix() + t.rwLocker.RLock() + // 提前 defaultCVMAuthExpire 获取重新获取临时密钥 + if t.expiredTime <= now+defaultCVMAuthExpire { + expiredTime := t.expiredTime + t.rwLocker.RUnlock() + secretID, secretKey, secretToken, err := t.UpdateCredential(now) + // 获取临时密钥失败但密钥未过期 + if err != nil && now < expiredTime { + err = nil + } + return secretID, secretKey, secretToken, err + } + defer t.rwLocker.RUnlock() + return t.secretID, t.secretKey, t.sessionToken, nil +} + +func (t *CVMCredentialsTransport) RoundTrip(req *http.Request) (*http.Response, error) { + ak, sk, token, err := t.GetCredential() + if err != nil { + return nil, err + } + req = cloneRequest(req) + // 增加 Authorization header + authTime := NewAuthTime(defaultAuthExpire) + AddAuthorizationHeader(ak, sk, token, req, authTime) + + resp, err := t.transport().RoundTrip(req) + return resp, err +} + +func (t *CVMCredentialsTransport) transport() http.RoundTripper { + if t.Transport != nil { + return t.Transport + } + return http.DefaultTransport +} diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket.go b/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket.go index f00072e5d3..db95f82709 100644 --- a/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket.go +++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket.go @@ -111,6 +111,17 @@ func (s *BucketService) Head(ctx context.Context) (*Response, error) { return resp, err } +func (s *BucketService) IsExist(ctx context.Context) (bool, error) { + _, err := s.Head(ctx) + if err == nil { + return true, nil + } + if IsNotFoundError(err) { + return false, nil + } + return false, err +} + // Bucket is the meta info of Bucket type Bucket struct { Name string diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/ci.go b/vendor/github.com/tencentyun/cos-go-sdk-v5/ci.go index 854f323336..de62459e32 100644 --- a/vendor/github.com/tencentyun/cos-go-sdk-v5/ci.go +++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/ci.go @@ -101,25 +101,43 @@ func (s *CIService) ImageProcess(ctx context.Context, name string, opt *ImagePro return &res, resp, err } +// ImageRecognitionOptions is the option of ImageAuditing type ImageRecognitionOptions struct { CIProcess string `url:"ci-process,omitempty"` DetectType string `url:"detect-type,omitempty"` + DetectUrl string `url:"detect-url,omitempty"` + Interval int `url:"interval,omitempty"` + MaxFrames int `url:"max-frames,omitempty"` + BizType string `url:"biz-type,omitempty"` } +// ImageRecognitionResult is the result of ImageRecognition/ImageAuditing type ImageRecognitionResult struct { XMLName xml.Name `xml:"RecognitionResult"` + Text string `xml:"Text,omitempty"` + Label string `xml:"Label,omitempty"` + Result int `xml:"Result,omitempty"` + Score int `xml:"Score,omitempty"` + SubLabel string `xml:"SubLabel,omitempty"` PornInfo *RecognitionInfo `xml:"PornInfo,omitempty"` TerroristInfo *RecognitionInfo `xml:"TerroristInfo,omitempty"` PoliticsInfo *RecognitionInfo `xml:"PoliticsInfo,omitempty"` AdsInfo *RecognitionInfo `xml:"AdsInfo,omitempty"` } + +// RecognitionInfo is the result of auditing scene type RecognitionInfo struct { - Code int `xml:"Code,omitempty"` - Msg string `xml:"Msg,omitempty"` - HitFlag int `xml:"HitFlag,omitempty"` - Score int `xml:"Score,omitempty"` - Label string `xml:"Label,omitempty"` - Count int `xml:"Count,omitempty"` + Code int `xml:"Code,omitempty"` + Msg string `xml:"Msg,omitempty"` + HitFlag int `xml:"HitFlag,omitempty"` + Score int `xml:"Score,omitempty"` + Label string `xml:"Label,omitempty"` + Count int `xml:"Count,omitempty"` + SubLabel string `xml:"SubLabel,omitempty"` + Keywords []string `xml:"Keywords,omitempty"` + OcrResults []OcrResult `xml:"OcrResults,omitempty"` + ObjectResults []ObjectResult `xml:"ObjectResults,omitempty"` + LibResults []LibResult `xml:"LibResults,omitempty"` } // 图片审核 https://cloud.tencent.com/document/product/460/37318 @@ -140,23 +158,108 @@ func (s *CIService) ImageRecognition(ctx context.Context, name string, DetectTyp return &res, resp, err } +// 图片审核 支持detect-url等全部参数 +func (s *CIService) ImageAuditing(ctx context.Context, name string, opt *ImageRecognitionOptions) (*ImageRecognitionResult, *Response, error) { + var res ImageRecognitionResult + sendOpt := sendOptions{ + baseURL: s.client.BaseURL.BucketURL, + uri: "/" + encodeURIComponent(name), + method: http.MethodGet, + optQuery: opt, + result: &res, + } + resp, err := s.client.send(ctx, &sendOpt) + return &res, resp, err +} + +// ImageAuditingInputOptions is the option of BatchImageAuditingOptions +type ImageAuditingInputOptions struct { + DataId string `xml:",omitempty"` + Object string `xml:",omitempty"` + Url string `xml:",omitempty"` + Interval int `xml:",omitempty"` + MaxFrames int `xml:",omitempty"` +} + +// ImageAuditingJobConf is the config of BatchImageAuditingOptions +type ImageAuditingJobConf struct { + DetectType string `xml:",omitempty"` + BizType string `xml:",omitempty"` +} + +// BatchImageAuditingOptions is the option of BatchImageAuditing +type BatchImageAuditingOptions struct { + XMLName xml.Name `xml:"Request"` + Input []ImageAuditingInputOptions `xml:"Input,omitempty"` + Conf *ImageAuditingJobConf `xml:"Conf"` +} + +// ImageRecognitionResult is the result of BatchImageAuditingJobResult +type ImageAuditingResult struct { + Code string `xml:",omitempty"` + Message string `xml:",omitempty"` + DataId string `xml:",omitempty"` + Object string `xml:",omitempty"` + Url string `xml:",omitempty"` + Text string `xml:",omitempty"` + Label string `xml:",omitempty"` + Result int `xml:",omitempty"` + Score int `xml:",omitempty"` + SubLabel string `xml:",omitempty"` + PornInfo *RecognitionInfo `xml:",omitempty"` + TerrorismInfo *RecognitionInfo `xml:",omitempty"` + PoliticsInfo *RecognitionInfo `xml:",omitempty"` + AdsInfo *RecognitionInfo `xml:",omitempty"` +} + +// BatchImageAuditingJobResult is the result of BatchImageAuditing +type BatchImageAuditingJobResult struct { + XMLName xml.Name `xml:"Response"` + JobsDetail []ImageAuditingResult `xml:",omitempty"` + RequestId string `xml:",omitempty"` +} + +// 图片批量审核接口 +func (s *CIService) BatchImageAuditing(ctx context.Context, opt *BatchImageAuditingOptions) (*BatchImageAuditingJobResult, *Response, error) { + var res BatchImageAuditingJobResult + sendOpt := sendOptions{ + baseURL: s.client.BaseURL.CIURL, + uri: "/image/auditing", + method: http.MethodPost, + body: opt, + result: &res, + } + resp, err := s.client.send(ctx, &sendOpt) + return &res, resp, err +} + +// PutVideoAuditingJobOptions is the option of PutVideoAuditingJob type PutVideoAuditingJobOptions struct { XMLName xml.Name `xml:"Request"` - InputObject string `xml:"Input>Object"` + InputObject string `xml:"Input>Object,omitempty"` + InputUrl string `xml:"Input>Url,omitempty"` + InputDataId string `xml:"Input>DataId,omitempty"` Conf *VideoAuditingJobConf `xml:"Conf"` } + +// VideoAuditingJobConf is the config of PutVideoAuditingJobOptions type VideoAuditingJobConf struct { - DetectType string `xml:",omitempty"` - Snapshot *PutVideoAuditingJobSnapshot `xml:",omitempty"` - Callback string `xml:",omitempty"` + DetectType string `xml:",omitempty"` + Snapshot *PutVideoAuditingJobSnapshot `xml:",omitempty"` + Callback string `xml:",omitempty"` + CallbackVersion string `xml:",omitempty"` + BizType string `xml:",omitempty"` + DetectContent int `xml:",omitempty"` } + +// PutVideoAuditingJobSnapshot is the snapshot config of VideoAuditingJobConf type PutVideoAuditingJobSnapshot struct { Mode string `xml:",omitempty"` Count int `xml:",omitempty"` TimeInterval float32 `xml:",omitempty"` - Start float32 `xml:",omitempty"` } +// PutVideoAuditingJobResult is the result of PutVideoAuditingJob type PutVideoAuditingJobResult struct { XMLName xml.Name `xml:"Response"` JobsDetail struct { @@ -164,7 +267,9 @@ type PutVideoAuditingJobResult struct { State string `xml:"State,omitempty"` CreationTime string `xml:"CreationTime,omitempty"` Object string `xml:"Object,omitempty"` + Url string `xml:"Url,omitempty"` } `xml:"JobsDetail,omitempty"` + RequestId string `xml:"RequestId,omitempty"` } // 视频审核-创建任务 https://cloud.tencent.com/document/product/460/46427 @@ -181,28 +286,55 @@ func (s *CIService) PutVideoAuditingJob(ctx context.Context, opt *PutVideoAuditi return &res, resp, err } +// GetVideoAuditingJobResult is the result of GetVideoAuditingJob type GetVideoAuditingJobResult struct { - XMLName xml.Name `xml:"Response"` - JobsDetail *AuditingJobDetail `xml:",omitempty"` - NonExistJobIds string `xml:",omitempty"` + XMLName xml.Name `xml:"Response"` + JobsDetail *AuditingJobDetail `xml:",omitempty"` + RequestId string `xml:",omitempty"` } + +// AuditingJobDetail is the detail of GetVideoAuditingJobResult type AuditingJobDetail struct { - Code string `xml:",omitempty"` - Message string `xml:",omitempty"` - JobId string `xml:",omitempty"` - State string `xml:",omitempty"` - CreationTime string `xml:",omitempty"` - Object string `xml:",omitempty"` - SnapshotCount string `xml:",omitempty"` - Result int `xml:",omitempty"` - PornInfo *RecognitionInfo `xml:",omitempty"` - TerrorismInfo *RecognitionInfo `xml:",omitempty"` - PoliticsInfo *RecognitionInfo `xml:",omitempty"` - AdsInfo *RecognitionInfo `xml:",omitempty"` - Snapshot *GetVideoAuditingJobSnapshot `xml:",omitempty"` -} + Code string `xml:",omitempty"` + Message string `xml:",omitempty"` + JobId string `xml:",omitempty"` + State string `xml:",omitempty"` + CreationTime string `xml:",omitempty"` + Object string `xml:",omitempty"` + Url string `xml:",omitempty"` + DataId string `xml:",omitempty"` + SnapshotCount string `xml:",omitempty"` + Label string `xml:",omitempty"` + Result int `xml:",omitempty"` + PornInfo *RecognitionInfo `xml:",omitempty"` + TerrorismInfo *RecognitionInfo `xml:",omitempty"` + PoliticsInfo *RecognitionInfo `xml:",omitempty"` + AdsInfo *RecognitionInfo `xml:",omitempty"` + Snapshot []GetVideoAuditingJobSnapshot `xml:",omitempty"` + AudioSection []AudioSectionResult `xml:",omitempty"` +} + +// GetVideoAuditingJobSnapshot is the snapshot result of AuditingJobDetail type GetVideoAuditingJobSnapshot struct { Url string `xml:",omitempty"` + Text string `xml:",omitempty"` + SnapshotTime int `xml:",omitempty"` + Label string `xml:",omitempty"` + Result int `xml:",omitempty"` + PornInfo *RecognitionInfo `xml:",omitempty"` + TerrorismInfo *RecognitionInfo `xml:",omitempty"` + PoliticsInfo *RecognitionInfo `xml:",omitempty"` + AdsInfo *RecognitionInfo `xml:",omitempty"` +} + +// AudioSectionResult is the audio section result of AuditingJobDetail/AudioAuditingJobDetail +type AudioSectionResult struct { + Url string `xml:",omitempty"` + Text string `xml:",omitempty"` + OffsetTime int `xml:",omitempty"` + Duration int `xml:",omitempty"` + Label string `xml:",omitempty"` + Result int `xml:",omitempty"` PornInfo *RecognitionInfo `xml:",omitempty"` TerrorismInfo *RecognitionInfo `xml:",omitempty"` PoliticsInfo *RecognitionInfo `xml:",omitempty"` @@ -222,17 +354,25 @@ func (s *CIService) GetVideoAuditingJob(ctx context.Context, jobid string) (*Get return &res, resp, err } +// PutAudioAuditingJobOptions is the option of PutAudioAuditingJob type PutAudioAuditingJobOptions struct { XMLName xml.Name `xml:"Request"` - InputObject string `xml:"Input>Object"` + InputObject string `xml:"Input>Object,omitempty"` + InputUrl string `xml:"Input>Url,omitempty"` + InputDataId string `xml:"Input>DataId,omitempty"` Conf *AudioAuditingJobConf `xml:"Conf"` } + +// AudioAuditingJobConf is the config of PutAudioAuditingJobOptions type AudioAuditingJobConf struct { - DetectType string `xml:",omitempty"` - Callback string `xml:",omitempty"` + DetectType string `xml:",omitempty"` + Callback string `xml:",omitempty"` + CallbackVersion string `xml:",omitempty"` + BizType string `xml:",omitempty"` } + +// PutAudioAuditingJobResult is the result of PutAudioAuditingJob type PutAudioAuditingJobResult PutVideoAuditingJobResult -type GetAudioAuditingJobResult GetVideoAuditingJobResult // 音频审核-创建任务 https://cloud.tencent.com/document/product/460/53395 func (s *CIService) PutAudioAuditingJob(ctx context.Context, opt *PutAudioAuditingJobOptions) (*PutAudioAuditingJobResult, *Response, error) { @@ -248,6 +388,33 @@ func (s *CIService) PutAudioAuditingJob(ctx context.Context, opt *PutAudioAuditi return &res, resp, err } +// GetAudioAuditingJobResult is the result of GetAudioAuditingJob +type GetAudioAuditingJobResult struct { + XMLName xml.Name `xml:"Response"` + JobsDetail *AudioAuditingJobDetail `xml:",omitempty"` + RequestId string `xml:",omitempty"` +} + +// AudioAuditingJobDetail is the detail of GetAudioAuditingJobResult +type AudioAuditingJobDetail struct { + Code string `xml:",omitempty"` + Message string `xml:",omitempty"` + JobId string `xml:",omitempty"` + State string `xml:",omitempty"` + CreationTime string `xml:",omitempty"` + Object string `xml:",omitempty"` + Url string `xml:",omitempty"` + DataId string `xml:",omitempty"` + AudioText string `xml:",omitempty"` + Label string `xml:",omitempty"` + Result int `xml:",omitempty"` + PornInfo *RecognitionInfo `xml:",omitempty"` + TerrorismInfo *RecognitionInfo `xml:",omitempty"` + PoliticsInfo *RecognitionInfo `xml:",omitempty"` + AdsInfo *RecognitionInfo `xml:",omitempty"` + Section []AudioSectionResult `xml:",omitempty"` +} + // 音频审核-查询任务 https://cloud.tencent.com/document/product/460/53396 func (s *CIService) GetAudioAuditingJob(ctx context.Context, jobid string) (*GetAudioAuditingJobResult, *Response, error) { var res GetAudioAuditingJobResult @@ -261,6 +428,345 @@ func (s *CIService) GetAudioAuditingJob(ctx context.Context, jobid string) (*Get return &res, resp, err } +// PutTextAuditingJobOptions is the option of PutTextAuditingJob +type PutTextAuditingJobOptions struct { + XMLName xml.Name `xml:"Request"` + InputObject string `xml:"Input>Object,omitempty"` + InputContent string `xml:"Input>Content,omitempty"` + InputDataId string `xml:"Input>DataId,omitempty"` + Conf *TextAuditingJobConf `xml:"Conf"` +} + +// TextAuditingJobConf is the config of PutAudioAuditingJobOptions +type TextAuditingJobConf struct { + DetectType string `xml:",omitempty"` + Callback string `xml:",omitempty"` + CallbackVersion string `xml:",omitempty"` + BizType string `xml:",omitempty"` +} + +// PutTextAuditingJobResult is the result of PutTextAuditingJob +type PutTextAuditingJobResult GetTextAuditingJobResult + +// 文本审核-创建任务 https://cloud.tencent.com/document/product/436/56289 +func (s *CIService) PutTextAuditingJob(ctx context.Context, opt *PutTextAuditingJobOptions) (*PutTextAuditingJobResult, *Response, error) { + var res PutTextAuditingJobResult + sendOpt := sendOptions{ + baseURL: s.client.BaseURL.CIURL, + uri: "/text/auditing", + method: http.MethodPost, + body: opt, + result: &res, + } + resp, err := s.client.send(ctx, &sendOpt) + return &res, resp, err +} + +// GetTextAuditingJobResult is the result of GetTextAuditingJob +type GetTextAuditingJobResult struct { + XMLName xml.Name `xml:"Response"` + JobsDetail *TextAuditingJobDetail `xml:",omitempty"` + RequestId string `xml:",omitempty"` +} + +// TextAuditingJobDetail is the detail of GetTextAuditingJobResult +type TextAuditingJobDetail struct { + Code string `xml:",omitempty"` + Message string `xml:",omitempty"` + JobId string `xml:",omitempty"` + State string `xml:",omitempty"` + CreationTime string `xml:",omitempty"` + Object string `xml:",omitempty"` + Url string `xml:",omitempty"` + DataId string `xml:",omitempty"` + Content string `xml:",omitempty"` + SectionCount int `xml:",omitempty"` + Label string `xml:",omitempty"` + Result int `xml:",omitempty"` + PornInfo *TextRecognitionInfo `xml:",omitempty"` + TerrorismInfo *TextRecognitionInfo `xml:",omitempty"` + PoliticsInfo *TextRecognitionInfo `xml:",omitempty"` + AdsInfo *TextRecognitionInfo `xml:",omitempty"` + IllegalInfo *TextRecognitionInfo `xml:",omitempty"` + AbuseInfo *TextRecognitionInfo `xml:",omitempty"` + Section []TextSectionResult `xml:",omitempty"` +} + +// TextRecognitionInfo +type TextRecognitionInfo struct { + Code int `xml:",omitempty"` + HitFlag int `xml:",omitempty"` + Score int `xml:",omitempty"` + Count int `xml:",omitempty"` + Keywords string `xml:",omitempty"` +} + +// TextSectionResult is the section result of TextAuditingJobDetail +type TextSectionResult struct { + StartByte int `xml:",omitempty"` + Label string `xml:",omitempty"` + Result int `xml:",omitempty"` + PornInfo *TextRecognitionInfo `xml:",omitempty"` + TerrorismInfo *TextRecognitionInfo `xml:",omitempty"` + PoliticsInfo *TextRecognitionInfo `xml:",omitempty"` + AdsInfo *TextRecognitionInfo `xml:",omitempty"` + IllegalInfo *TextRecognitionInfo `xml:",omitempty"` + AbuseInfo *TextRecognitionInfo `xml:",omitempty"` +} + +// 文本审核-查询任务 https://cloud.tencent.com/document/product/436/56288 +func (s *CIService) GetTextAuditingJob(ctx context.Context, jobid string) (*GetTextAuditingJobResult, *Response, error) { + var res GetTextAuditingJobResult + sendOpt := sendOptions{ + baseURL: s.client.BaseURL.CIURL, + uri: "/text/auditing/" + jobid, + method: http.MethodGet, + result: &res, + } + resp, err := s.client.send(ctx, &sendOpt) + return &res, resp, err +} + +// PutDocumentAuditingJobOptions is the option of PutDocumentAuditingJob +type PutDocumentAuditingJobOptions struct { + XMLName xml.Name `xml:"Request"` + InputObject string `xml:"Input>Object,omitempty"` + InputUrl string `xml:"Input>Url,omitempty"` + InputType string `xml:"Input>Type,omitempty"` + InputDataId string `xml:"Input>DataId,omitempty"` + Conf *DocumentAuditingJobConf `xml:"Conf"` +} + +// DocumentAuditingJobConf is the config of PutDocumentAuditingJobOptions +type DocumentAuditingJobConf struct { + DetectType string `xml:",omitempty"` + Callback string `xml:",omitempty"` + BizType string `xml:",omitempty"` +} + +// PutDocumentAuditingJobResult is the result of PutDocumentAuditingJob +type PutDocumentAuditingJobResult PutVideoAuditingJobResult + +// 文档审核-创建任务 https://cloud.tencent.com/document/product/436/59381 +func (s *CIService) PutDocumentAuditingJob(ctx context.Context, opt *PutDocumentAuditingJobOptions) (*PutDocumentAuditingJobResult, *Response, error) { + var res PutDocumentAuditingJobResult + sendOpt := sendOptions{ + baseURL: s.client.BaseURL.CIURL, + uri: "/document/auditing", + method: http.MethodPost, + body: opt, + result: &res, + } + resp, err := s.client.send(ctx, &sendOpt) + return &res, resp, err +} + +// GetDocumentAuditingJobResult is the result of GetDocumentAuditingJob +type GetDocumentAuditingJobResult struct { + XMLName xml.Name `xml:"Response"` + JobsDetail *DocumentAuditingJobDetail `xml:",omitempty"` + RequestId string `xml:",omitempty"` +} + +// DocumentAuditingJobDetail is the detail of GetDocumentAuditingJobResult +type DocumentAuditingJobDetail struct { + Code string `xml:",omitempty"` + Message string `xml:",omitempty"` + JobId string `xml:",omitempty"` + State string `xml:",omitempty"` + CreationTime string `xml:",omitempty"` + Object string `xml:",omitempty"` + Url string `xml:",omitempty"` + DataId string `xml:",omitempty"` + PageCount int `xml:",omitempty"` + Label string `xml:",omitempty"` + Suggestion int `xml:",omitempty"` + Labels *DocumentResultInfo `xml:",omitempty"` + PageSegment *DocumentPageSegmentInfo `xml:",omitempty"` +} + +// DocumentResultInfo +type DocumentResultInfo struct { + PornInfo *RecognitionInfo `xml:",omitempty"` + TerrorismInfo *RecognitionInfo `xml:",omitempty"` + PoliticsInfo *RecognitionInfo `xml:",omitempty"` + AdsInfo *RecognitionInfo `xml:",omitempty"` +} + +// DocumentPageSegmentInfo +type DocumentPageSegmentInfo struct { + Results []DocumentPageSegmentResultResult `xml:",omitempty"` +} + +// DocumentPageSegmentResultResult +type DocumentPageSegmentResultResult struct { + Url string `xml:",omitempty"` + Text string `xml:",omitempty"` + PageNumber int `xml:",omitempty"` + SheetNumber int `xml:",omitempty"` + Label string `xml:",omitempty"` + Suggestion int `xml:",omitempty"` + PornInfo *RecognitionInfo `xml:",omitempty"` + TerrorismInfo *RecognitionInfo `xml:",omitempty"` + PoliticsInfo *RecognitionInfo `xml:",omitempty"` + AdsInfo *RecognitionInfo `xml:",omitempty"` +} + +// OcrResult +type OcrResult struct { + Text string `xml:"Text,omitempty"` + Keywords []string `xml:"Keywords,omitempty"` + Location *Location `xml:"Location,omitempty"` +} + +// ObjectResult +type ObjectResult struct { + Name string `xml:"Name,omitempty"` + Location *Location `xml:"Location,omitempty"` +} + +// LibResult +type LibResult struct { + ImageId string `xml:"ImageId"` + Score uint32 `xml:"Score"` +} + +// Location +type Location struct { + X float64 `xml:"X,omitempty"` // 左上角横坐标 + Y float64 `xml:"Y,omitempty"` // 左上角纵坐标 + Width float64 `xml:"Width,omitempty"` // 宽度 + Height float64 `xml:"Height,omitempty"` // 高度 + Rotate float64 `xml:"Rotate,omitempty"` // 检测框的旋转角度 +} + +// 文档审核-查询任务 https://cloud.tencent.com/document/product/436/59382 +func (s *CIService) GetDocumentAuditingJob(ctx context.Context, jobid string) (*GetDocumentAuditingJobResult, *Response, error) { + var res GetDocumentAuditingJobResult + sendOpt := sendOptions{ + baseURL: s.client.BaseURL.CIURL, + uri: "/document/auditing/" + jobid, + method: http.MethodGet, + result: &res, + } + resp, err := s.client.send(ctx, &sendOpt) + return &res, resp, err +} + +// PutWebpageAuditingJobOptions is the option of PutWebpageAuditingJob +type PutWebpageAuditingJobOptions struct { + XMLName xml.Name `xml:"Request"` + InputUrl string `xml:"Input>Url,omitempty"` + Conf *WebpageAuditingJobConf `xml:"Conf"` +} + +// WebpageAuditingJobConf is the config of PutWebpageAuditingJobOptions +type WebpageAuditingJobConf struct { + DetectType string `xml:",omitempty"` + Callback string `xml:",omitempty"` + ReturnHighlightHtml bool `xml:",omitempty"` +} + +// PutWebpageAuditingJobResult is the result of PutWebpageAuditingJob +type PutWebpageAuditingJobResult PutVideoAuditingJobResult + +// 网页审核-创建任务 https://cloud.tencent.com/document/product/436/63958 +func (s *CIService) PutWebpageAuditingJob(ctx context.Context, opt *PutWebpageAuditingJobOptions) (*PutWebpageAuditingJobResult, *Response, error) { + var res PutWebpageAuditingJobResult + sendOpt := sendOptions{ + baseURL: s.client.BaseURL.CIURL, + uri: "/webpage/auditing", + method: http.MethodPost, + body: opt, + result: &res, + } + resp, err := s.client.send(ctx, &sendOpt) + return &res, resp, err +} + +// GetWebpageAuditingJobResult is the result of GetWebpageAuditingJob +type GetWebpageAuditingJobResult struct { + XMLName xml.Name `xml:"Response"` + JobsDetail *WebpageAuditingJobDetail `xml:",omitempty"` +} + +// WebpageAuditingJobDetail is the detail of GetWebpageAuditingJobResult +type WebpageAuditingJobDetail struct { + Code string `xml:",omitempty"` + Message string `xml:",omitempty"` + JobId string `xml:",omitempty"` + State string `xml:",omitempty"` + CreationTime string `xml:",omitempty"` + Url string `xml:",omitempty"` + Labels *WebpageResultInfo `xml:",omitempty"` + PageCount int `xml:",omitempty"` + Suggestion int `xml:",omitempty"` + ImageResults *WebpageImageResults `xml:",omitempty"` + TextResults *WebpageTextResults `xml:",omitempty"` + HighlightHtml string `xml:",omitempty"` +} + +// WebpageResultInfo +type WebpageResultInfo struct { + PornInfo *RecognitionInfo `xml:",omitempty"` + TerrorismInfo *RecognitionInfo `xml:",omitempty"` + PoliticsInfo *RecognitionInfo `xml:",omitempty"` + AdsInfo *RecognitionInfo `xml:",omitempty"` +} + +// WebpageImageResults +type WebpageImageResults struct { + Results []WebpageImageResult `xml:",omitempty"` +} + +// WebpageImageResult +type WebpageImageResult struct { + Url string `xml:",omitempty"` + Text string `xml:",omitempty"` + Label string `xml:",omitempty"` + PageNumber int `xml:",omitempty"` + SheetNumber int `xml:",omitempty"` + Suggestion int `xml:",omitempty"` + PornInfo *RecognitionInfo `xml:",omitempty"` + TerrorismInfo *RecognitionInfo `xml:",omitempty"` + PoliticsInfo *RecognitionInfo `xml:",omitempty"` + AdsInfo *RecognitionInfo `xml:",omitempty"` +} + +// WebpageTextResults +type WebpageTextResults struct { + Results []WebpageTextResult `xml:",omitempty"` +} + +// WebpageTextResult +type WebpageTextResult struct { + Text string `xml:",omitempty"` + Label string `xml:",omitempty"` + Result int `xml:",omitempty"` + PageNumber int `xml:",omitempty"` + SheetNumber int `xml:",omitempty"` + Suggestion int `xml:",omitempty"` + PornInfo *TextRecognitionInfo `xml:",omitempty"` + TerrorismInfo *TextRecognitionInfo `xml:",omitempty"` + PoliticsInfo *TextRecognitionInfo `xml:",omitempty"` + AdsInfo *TextRecognitionInfo `xml:",omitempty"` + IllegalInfo *TextRecognitionInfo `xml:",omitempty"` + AbuseInfo *TextRecognitionInfo `xml:",omitempty"` +} + +// 网页审核-查询任务 https://cloud.tencent.com/document/product/436/63959 +func (s *CIService) GetWebpageAuditingJob(ctx context.Context, jobid string) (*GetWebpageAuditingJobResult, *Response, error) { + var res GetWebpageAuditingJobResult + sendOpt := sendOptions{ + baseURL: s.client.BaseURL.CIURL, + uri: "/webpage/auditing/" + jobid, + method: http.MethodGet, + result: &res, + } + resp, err := s.client.send(ctx, &sendOpt) + return &res, resp, err +} + // 图片持久化处理-上传时处理 https://cloud.tencent.com/document/product/460/18147 // 盲水印-上传时添加 https://cloud.tencent.com/document/product/460/19017 // 二维码识别-上传时识别 https://cloud.tencent.com/document/product/460/37513 @@ -323,9 +829,9 @@ func (s *CIService) PutFromFile(ctx context.Context, name string, filePath strin func (s *CIService) Get(ctx context.Context, name string, operation string, opt *ObjectGetOptions, id ...string) (*Response, error) { var u string if len(id) == 1 { - u = fmt.Sprintf("/%s?versionId=%s&%s", encodeURIComponent(name), id[0], operation) + u = fmt.Sprintf("/%s?versionId=%s&%s", encodeURIComponent(name), id[0], encodeURIComponent(operation)) } else if len(id) == 0 { - u = fmt.Sprintf("/%s?%s", encodeURIComponent(name), operation) + u = fmt.Sprintf("/%s?%s", encodeURIComponent(name), encodeURIComponent(operation)) } else { return nil, errors.New("wrong params") } diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/ci_doc.go b/vendor/github.com/tencentyun/cos-go-sdk-v5/ci_doc.go index 74df304c0a..52d40f79a4 100644 --- a/vendor/github.com/tencentyun/cos-go-sdk-v5/ci_doc.go +++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/ci_doc.go @@ -31,14 +31,18 @@ type DocProcessJobDocProcess struct { } type DocProcessJobDocProcessResult struct { - FailPageCount int `xml:",omitempty"` - SuccPageCount int `xml:"SuccPageCount,omitempty"` - TaskId string `xml:"TaskId,omitempty"` - TgtType string `xml:"TgtType,omitempty"` - TotalPageCount int `xml:"TotalPageCount,omitempty"` - PageInfo struct { - PageNo int `xml:"PageNo,omitempty"` - TgtUri string `xml:"TgtUri,omitempty"` + FailPageCount int `xml:",omitempty"` + SuccPageCount int `xml:"SuccPageCount,omitempty"` + TaskId string `xml:"TaskId,omitempty"` + TgtType string `xml:"TgtType,omitempty"` + TotalPageCount int `xml:"TotalPageCount,omitempty"` + TotalSheetCount int `xml:"TotalSheetCount,omitempty"` + PageInfo []struct { + PageNo int `xml:"PageNo,omitempty"` + TgtUri string `xml:"TgtUri,omitempty"` + XSheetPics int `xml:"X-SheetPics,omitempty"` + PicIndex int `xml:"PicIndex,omitempty"` + PicNum int `xml:"PicNum,omitempty"` } `xml:"PageInfo,omitempty"` } diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/ci_media.go b/vendor/github.com/tencentyun/cos-go-sdk-v5/ci_media.go index c7bbd215a3..d59ec0e38c 100644 --- a/vendor/github.com/tencentyun/cos-go-sdk-v5/ci_media.go +++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/ci_media.go @@ -3,6 +3,7 @@ package cos import ( "context" "encoding/xml" + "fmt" "net/http" ) @@ -11,9 +12,10 @@ type JobInput struct { } type JobOutput struct { - Region string `xml:"Region,omitempty"` - Bucket string `xml:"Bucket,omitempty"` - Object string `xml:"Object,omitempty"` + Region string `xml:"Region,omitempty"` + Bucket string `xml:"Bucket,omitempty"` + Object string `xml:"Object,omitempty"` + SpriteObject string `xml:"SpriteObject,omitempty"` } type Container struct { @@ -109,13 +111,61 @@ type ConcatTemplate struct { Container *Container `xml:"Container,omitempty"` Index string `xml:"Index,omitempty"` } + +type SpriteSnapshotConfig struct { + CellHeight string `xml:"CellHeight,omitempty"` + CellWidth string `xml:"CellWidth,omitempty"` + Color string `xml:"Color,omitempty"` + Columns string `xml:"Columns,omitempty"` + Lines string `xml:"Lines,omitempty"` + Margin string `xml:"Margin,omitempty"` + Padding string `xml:"Padding,omitempty"` +} +type Snapshot struct { + Mode string `xml:"Mode,omitempty"` + Start string `xml:"Start,omitempty"` + TimeInterval string `xml:"TimeInterval,omitempty"` + Count string `xml:"Count,omitempty"` + Width string `xml:"Width,omitempty"` + Height string `xml:"Height,omitempty"` + SnapshotOutMode string `xml:"SnapshotOutMode,omitempty"` + SpriteSnapshotConfig *SpriteSnapshotConfig `xml:"SpriteSnapshotConfig,omitempty"` +} + +// 有意和转码区分,两种任务关注的参数不一样避免干扰 +type AnimationVideo struct { + Codec string `xml:"Codec"` + Width string `xml:"Width"` + Height string `xml:"Height"` + Fps string `xml:"Fps"` + AnimateOnlyKeepKeyFrame string `xml:"AnimateOnlyKeepKeyFrame"` + AnimateTimeIntervalOfFrame string `xml:"AnimateTimeIntervalOfFrame"` + AnimateFramesPerSecond string `xml:"AnimateFramesPerSecond"` + Quality string `xml:"Quality"` +} + +type Animation struct { + Container *Container `xml:"Container,omitempty"` + Video *AnimationVideo `xml:"Video,omitempty"` + TimeInterval *TimeInterval `xml:"TimeInterval,omitempty"` +} + +type Segment struct { + Format string `xml:"Format,omitempty"` + Duration string `xml:"Duration,omitempty"` +} + type MediaProcessJobOperation struct { + Tag string `xml:"Tag,omitempty"` Output *JobOutput `xml:"Output,omitempty"` Transcode *Transcode `xml:"Transcode,omitempty"` Watermark *Watermark `xml:"Watermark,omitempty"` TemplateId string `xml:"TemplateId,omitempty"` WatermarkTemplateId []string `xml:"WatermarkTemplateId,omitempty"` ConcatTemplate *ConcatTemplate `xml:"ConcatTemplate,omitempty"` + Snapshot *Snapshot `xml:"Snapshot,omitempty"` + Animation *Animation `xml:"Animation,omitempty"` + Segment *Segment `xml:"Segment,omitempty"` } type CreateMediaJobsOptions struct { @@ -140,8 +190,163 @@ type MediaProcessJobDetail struct { } type CreateMediaJobsResult struct { - XMLName xml.Name `xml:"Response"` - JobsDetail MediaProcessJobDetail `xml:"JobsDetail,omitempty"` + XMLName xml.Name `xml:"Response"` + JobsDetail *MediaProcessJobDetail `xml:"JobsDetail,omitempty"` +} + +type CreateMultiMediaJobsOptions struct { + XMLName xml.Name `xml:"Request"` + Tag string `xml:"Tag,omitempty"` + Input *JobInput `xml:"Input,omitempty"` + Operation []MediaProcessJobOperation `xml:"Operation,omitempty"` + QueueId string `xml:"QueueId,omitempty"` + CallBack string `xml:"CallBack,omitempty"` +} + +type CreateMultiMediaJobsResult struct { + XMLName xml.Name `xml:"Response"` + JobsDetail []MediaProcessJobDetail `xml:"JobsDetail,omitempty"` +} + +type MediaProcessJobsNotifyBody struct { + XMLName xml.Name `xml:"Response"` + EventName string `xml:"EventName"` + JobsDetail struct { + Code string `xml:"Code"` + CreationTime string `xml:"CreationTime"` + EndTime string `xml:"EndTime"` + Input struct { + BucketId string `xml:"BucketId"` + Object string `xml:"Object"` + Region string `xml:"Region"` + } `xml:"Input"` + JobId string `xml:"JobId"` + Message string `xml:"Message"` + Operation struct { + MediaInfo struct { + Format struct { + Text string `xml:",chardata"` + Bitrate string `xml:"Bitrate"` + Duration string `xml:"Duration"` + FormatLongName string `xml:"FormatLongName"` + FormatName string `xml:"FormatName"` + NumProgram string `xml:"NumProgram"` + NumStream string `xml:"NumStream"` + Size string `xml:"Size"` + StartTime string `xml:"StartTime"` + } `xml:"Format"` + Stream struct { + Audio []struct { + Bitrate string `xml:"Bitrate"` + Channel string `xml:"Channel"` + ChannelLayout string `xml:"ChannelLayout"` + CodecLongName string `xml:"CodecLongName"` + CodecName string `xml:"CodecName"` + CodecTag string `xml:"CodecTag"` + CodecTagString string `xml:"CodecTagString"` + CodecTimeBase string `xml:"CodecTimeBase"` + Duration string `xml:"Duration"` + Index string `xml:"Index"` + Language string `xml:"Language"` + SampleFmt string `xml:"SampleFmt"` + SampleRate string `xml:"SampleRate"` + StartTime string `xml:"StartTime"` + Timebase string `xml:"Timebase"` + } `xml:"Audio"` + Subtitle string `xml:"Subtitle"` + Video []struct { + AvgFps string `xml:"AvgFps"` + Bitrate string `xml:"Bitrate"` + CodecLongName string `xml:"CodecLongName"` + CodecName string `xml:"CodecName"` + CodecTag string `xml:"CodecTag"` + CodecTagString string `xml:"CodecTagString"` + CodecTimeBase string `xml:"CodecTimeBase"` + Dar string `xml:"Dar"` + Duration string `xml:"Duration"` + Fps string `xml:"Fps"` + HasBFrame string `xml:"HasBFrame"` + Height string `xml:"Height"` + Index string `xml:"Index"` + Language string `xml:"Language"` + Level string `xml:"Level"` + NumFrames string `xml:"NumFrames"` + PixFormat string `xml:"PixFormat"` + Profile string `xml:"Profile"` + RefFrames string `xml:"RefFrames"` + Rotation string `xml:"Rotation"` + Sar string `xml:"Sar"` + StartTime string `xml:"StartTime"` + Timebase string `xml:"Timebase"` + Width string `xml:"Width"` + } `xml:"Video"` + } `xml:"Stream"` + } `xml:"MediaInfo"` + MediaResult struct { + OutputFile struct { + Bucket string `xml:"Bucket"` + ObjectName []string `xml:"ObjectName"` + ObjectPrefix string `xml:"ObjectPrefix"` + Region string `xml:"Region"` + } `xml:"OutputFile"` + } `xml:"MediaResult"` + Output struct { + Bucket string `xml:"Bucket"` + Object string `xml:"Object"` + Region string `xml:"Region"` + } `xml:"Output"` + TemplateId string `xml:"TemplateId"` + TemplateName string `xml:"TemplateName"` + } `xml:"Operation"` + QueueId string `xml:"QueueId"` + StartTime string `xml:"StartTime"` + State string `xml:"State"` + Tag string `xml:"Tag"` + } `xml:"JobsDetail"` +} + +type WorkflowExecutionNotifyBody struct { + XMLName xml.Name `xml:"Response"` + EventName string `xml:"EventName"` + WorkflowExecution struct { + RunId string `xml:"RunId"` + BucketId string `xml:"BucketId"` + Object string `xml:"Object"` + CosHeaders []struct { + Key string `xml:"Key"` + Value string `xml:"Value"` + } `xml:"CosHeaders"` + WorkflowId string `xml:"WorkflowId"` + WorkflowName string `xml:"WorkflowName"` + CreateTime string `xml:"CreateTime"` + State string `xml:"State"` + Tasks []struct { + Type string `xml:"Type"` + CreateTime string `xml:"CreateTime"` + EndTime string `xml:"EndTime"` + State string `xml:"State"` + JobId string `xml:"JobId"` + Name string `xml:"Name"` + TemplateId string `xml:"TemplateId"` + TemplateName string `xml:"TemplateName"` + TranscodeTemplateId string `xml:"TranscodeTemplateId,omitempty"` + TranscodeTemplateName string `xml:"TranscodeTemplateName,omitempty"` + HdrMode string `xml:"HdrMode,omitempty"` + } `xml:"Tasks"` + } `xml:"WorkflowExecution"` +} + +func (s *CIService) CreateMultiMediaJobs(ctx context.Context, opt *CreateMultiMediaJobsOptions) (*CreateMultiMediaJobsResult, *Response, error) { + var res CreateMultiMediaJobsResult + sendOpt := sendOptions{ + baseURL: s.client.BaseURL.CIURL, + uri: "/jobs", + method: http.MethodPost, + body: opt, + result: &res, + } + resp, err := s.client.send(ctx, &sendOpt) + return &res, resp, err } func (s *CIService) CreateMediaJobs(ctx context.Context, opt *CreateMediaJobsOptions) (*CreateMediaJobsResult, *Response, error) { @@ -187,9 +392,9 @@ type DescribeMediaJobsOptions struct { } type DescribeMediaJobsResult struct { - XMLName xml.Name `xml:"Response"` - JobsDetail []DocProcessJobDetail `xml:"JobsDetail,omitempty"` - NextToken string `xml:"NextToken,omitempty"` + XMLName xml.Name `xml:"Response"` + JobsDetail []MediaProcessJobDetail `xml:"JobsDetail,omitempty"` + NextToken string `xml:"NextToken,omitempty"` } func (s *CIService) DescribeMediaJobs(ctx context.Context, opt *DescribeMediaJobsOptions) (*DescribeMediaJobsResult, *Response, error) { @@ -302,6 +507,7 @@ type MediaProcessBucket struct { CreateTime string `xml:"CreateTime,omitempty"` } +// 媒体bucket接口 https://cloud.tencent.com/document/product/436/48988 func (s *CIService) DescribeMediaProcessBuckets(ctx context.Context, opt *DescribeMediaProcessBucketsOptions) (*DescribeMediaProcessBucketsResult, *Response, error) { var res DescribeMediaProcessBucketsResult sendOpt := sendOptions{ @@ -314,3 +520,123 @@ func (s *CIService) DescribeMediaProcessBuckets(ctx context.Context, opt *Descri resp, err := s.client.send(ctx, &sendOpt) return &res, resp, err } + +type GetMediaInfoResult struct { + XMLName xml.Name `xml:"Response"` + MediaInfo struct { + Format struct { + Bitrate float32 `xml:"Bitrate"` + Duration float32 `xml:"Duration"` + FormatLongName string `xml:"FormatLongName"` + FormatName string `xml:"FormatName"` + NumProgram int `xml:"NumProgram"` + NumStream int `xml:"NumStream"` + Size int `xml:"Size"` + StartTime float32 `xml:"StartTime"` + } `xml:"Format"` + Stream struct { + Audio []struct { + Index int `xml:"Index"` + CodecName string `xml:"CodecName"` + CodecLongName string `xml:"CodecLongName"` + CodecTimeBase string `xml:"CodecTimeBase"` + CodecTagString string `xml:"CodecTagString"` + CodecTag string `xml:"CodecTag"` + SampleFmt string `xml:"SampleFmt"` + SampleRate int `xml:"SampleRate"` + Channel int `xml:"Channel"` + ChannelLayout string `xml:"ChannelLayout"` + Timebase string `xml:"Timebase"` + StartTime float32 `xml:"StartTime"` + Duration float32 `xml:"Duration"` + Bitrate float32 `xml:"Bitrate"` + Language string `xml:"Language"` + } `xml:"Audio"` + Subtitle struct { + Index int `xml:"Index"` + Language string `xml:"Language"` + } `xml:"Subtitle"` + Video struct { + Index int `xml:"Index"` + CodecName string `xml:"CodecName"` + CodecLongName string `xml:"CodecLongName"` + CodecTimeBase string `xml:"CodecTimeBase"` + CodecTagString string `xml:"CodecTagString"` + CodecTag string `xml:"CodecTag"` + Profile string `xml:"Profile"` + Height int `xml:"Height"` + Width int `xml:"Width"` + HasBFrame int `xml:"HasBFrame"` + RefFrames int `xml:"RefFrames"` + Sar string `xml:"Sar"` + Dar string `xml:"Dar"` + PixFormat string `xml:"PixFormat"` + FieldOrder string `xml:"FieldOrder"` + Level int `xml:"Level"` + Fps float32 `xml:"Fps"` + AvgFps string `xml:"AvgFps"` + Timebase string `xml:"Timebase"` + StartTime float32 `xml:"StartTime"` + Duration float32 `xml:"Duration"` + Bitrate float32 `xml:"Bitrate"` + NumFrames int `xml:"NumFrames"` + Language string `xml:"Language"` + } `xml:"Video"` + } `xml:"Stream"` + } `xml:"MediaInfo"` +} + +// 媒体信息接口 https://cloud.tencent.com/document/product/436/55672 +func (s *CIService) GetMediaInfo(ctx context.Context, name string, opt *ObjectGetOptions, id ...string) (*GetMediaInfoResult, *Response, error) { + var u string + if len(id) == 1 { + u = fmt.Sprintf("/%s?versionId=%s&ci-process=videoinfo", encodeURIComponent(name), id[0]) + } else if len(id) == 0 { + u = fmt.Sprintf("/%s?ci-process=videoinfo", encodeURIComponent(name)) + } else { + return nil, nil, fmt.Errorf("wrong params") + } + + var res GetMediaInfoResult + sendOpt := sendOptions{ + baseURL: s.client.BaseURL.BucketURL, + uri: u, + method: http.MethodGet, + optQuery: opt, + optHeader: opt, + result: &res, + } + resp, err := s.client.send(ctx, &sendOpt) + return &res, resp, err +} + +type GetSnapshotOptions struct { + Time float32 `url:"time,omitempty"` + Height int `url:"height,omitempty"` + Width int `url:"width,omitempty"` + Format string `url:"format,omitempty"` + Rotate string `url:"rotate,omitempty"` + Mode string `url:"mode,omitempty"` +} + +// 媒体截图接口 https://cloud.tencent.com/document/product/436/55671 +func (s *CIService) GetSnapshot(ctx context.Context, name string, opt *GetSnapshotOptions, id ...string) (*Response, error) { + var u string + if len(id) == 1 { + u = fmt.Sprintf("/%s?versionId=%s&ci-process=snapshot", encodeURIComponent(name), id[0]) + } else if len(id) == 0 { + u = fmt.Sprintf("/%s?ci-process=snapshot", encodeURIComponent(name)) + } else { + return nil, fmt.Errorf("wrong params") + } + + sendOpt := sendOptions{ + baseURL: s.client.BaseURL.BucketURL, + uri: u, + method: http.MethodGet, + optQuery: opt, + disableCloseBody: true, + } + resp, err := s.client.send(ctx, &sendOpt) + return resp, err +} diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/cos.go b/vendor/github.com/tencentyun/cos-go-sdk-v5/cos.go index 8a9e8204ec..4a9ef889f4 100644 --- a/vendor/github.com/tencentyun/cos-go-sdk-v5/cos.go +++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/cos.go @@ -13,6 +13,7 @@ import ( "reflect" "strings" "text/template" + "time" "strconv" @@ -22,7 +23,7 @@ import ( const ( // Version current go sdk version - Version = "0.7.31" + Version = "0.7.33" userAgent = "cos-go-sdk-v5/" + Version contentTypeXML = "application/xml" defaultServiceBaseURL = "http://service.cos.myqcloud.com" @@ -44,6 +45,8 @@ type BaseURL struct { BatchURL *url.URL // 访问 CI 的基础 URL CIURL *url.URL + // 访问 Fetch Task 的基础 URL + FetchURL *url.URL } // NewBucketURL 生成 BaseURL 所需的 BucketURL @@ -51,12 +54,18 @@ type BaseURL struct { // bucketName: bucket名称, bucket的命名规则为{name}-{appid} ,此处填写的存储桶名称必须为此格式 // Region: 区域代码: ap-beijing-1,ap-beijing,ap-shanghai,ap-guangzhou... // secure: 是否使用 https -func NewBucketURL(bucketName, region string, secure bool) *url.URL { +func NewBucketURL(bucketName, region string, secure bool) (*url.URL, error) { schema := "https" if !secure { schema = "http" } + if region == "" { + return nil, fmt.Errorf("region[%v] is invalid", region) + } + if bucketName == "" || !strings.ContainsAny(bucketName, "-") { + return nil, fmt.Errorf("bucketName[%v] is invalid", bucketName) + } w := bytes.NewBuffer(nil) bucketURLTemplate.Execute(w, struct { Schema string @@ -67,12 +76,18 @@ func NewBucketURL(bucketName, region string, secure bool) *url.URL { }) u, _ := url.Parse(w.String()) - return u + return u, nil } +type RetryOptions struct { + Count int + Interval time.Duration + StatusCode []int +} type Config struct { EnableCRC bool RequestBodyClose bool + RetryOpt RetryOptions } // Client is a client manages communication with the COS API. @@ -110,6 +125,7 @@ func NewClient(uri *BaseURL, httpClient *http.Client) *Client { baseURL.ServiceURL = uri.ServiceURL baseURL.BatchURL = uri.BatchURL baseURL.CIURL = uri.CIURL + baseURL.FetchURL = uri.FetchURL } if baseURL.ServiceURL == nil { baseURL.ServiceURL, _ = url.Parse(defaultServiceBaseURL) @@ -122,6 +138,10 @@ func NewClient(uri *BaseURL, httpClient *http.Client) *Client { Conf: &Config{ EnableCRC: true, RequestBodyClose: false, + RetryOpt: RetryOptions{ + Count: 3, + Interval: time.Duration(0), + }, }, } c.common.client = c @@ -306,14 +326,31 @@ func (c *Client) doRetry(ctx context.Context, opt *sendOptions) (resp *Response, return } } + count := 1 + if count < c.Conf.RetryOpt.Count { + count = c.Conf.RetryOpt.Count + } nr := 0 - for nr < 3 { + interval := c.Conf.RetryOpt.Interval + for nr < count { resp, err = c.send(ctx, opt) if err != nil { if resp != nil && resp.StatusCode <= 499 { - break + dobreak := true + for _, v := range c.Conf.RetryOpt.StatusCode { + if resp.StatusCode == v { + dobreak = false + break + } + } + if dobreak { + break + } } nr++ + if interval > 0 && nr < count { + time.Sleep(interval) + } continue } break diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/error.go b/vendor/github.com/tencentyun/cos-go-sdk-v5/error.go index c10133912d..8f0a7c8e6a 100644 --- a/vendor/github.com/tencentyun/cos-go-sdk-v5/error.go +++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/error.go @@ -1,10 +1,13 @@ package cos import ( + "encoding/json" "encoding/xml" "fmt" "io/ioutil" "net/http" + "strconv" + "strings" ) // ErrorResponse 包含 API 返回的错误信息 @@ -39,6 +42,12 @@ func (r *ErrorResponse) Error() string { r.Response.StatusCode, r.Code, r.Message, RequestID, TraceID) } +type jsonError struct { + Code int `json:"code,omitempty"` + Message string `json:"message,omitempty"` + RequestID string `json:"request_id,omitempty"` +} + // 检查 response 是否是出错时的返回的 response func checkResponse(r *http.Response) error { if c := r.StatusCode; 200 <= c && c <= 299 { @@ -49,6 +58,18 @@ func checkResponse(r *http.Response) error { if err == nil && data != nil { xml.Unmarshal(data, errorResponse) } + // 是否为 json 格式 + if errorResponse.Code == "" { + ctype := strings.TrimLeft(r.Header.Get("Content-Type"), " ") + if strings.HasPrefix(ctype, "application/json") { + var jerror jsonError + json.Unmarshal(data, &jerror) + errorResponse.Code = strconv.Itoa(jerror.Code) + errorResponse.Message = jerror.Message + errorResponse.RequestID = jerror.RequestID + } + + } return errorResponse } diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/helper.go b/vendor/github.com/tencentyun/cos-go-sdk-v5/helper.go index 8fe97feac7..7af895c47f 100644 --- a/vendor/github.com/tencentyun/cos-go-sdk-v5/helper.go +++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/helper.go @@ -316,15 +316,18 @@ func FormatRangeOptions(opt *RangeOptions) string { } return "" } - func GetRangeOptions(opt *ObjectGetOptions) (*RangeOptions, error) { if opt == nil || opt.Range == "" { return nil, nil } + return GetRange(opt.Range) +} + +func GetRange(rangeStr string) (*RangeOptions, error) { // bytes=M-N - slices := strings.Split(opt.Range, "=") + slices := strings.Split(rangeStr, "=") if len(slices) != 2 || slices[0] != "bytes" { - return nil, fmt.Errorf("Invalid Parameter Range: %v", opt.Range) + return nil, fmt.Errorf("Invalid Parameter Range: %v", rangeStr) } // byte=M-N, X-Y fSlice := strings.Split(slices[1], ",") @@ -334,13 +337,13 @@ func GetRangeOptions(opt *ObjectGetOptions) (*RangeOptions, error) { var ropt RangeOptions sted := strings.Split(rstr, "-") if len(sted) != 2 { - return nil, fmt.Errorf("Invalid Parameter Range: %v", opt.Range) + return nil, fmt.Errorf("Invalid Parameter Range: %v", rangeStr) } // M if len(sted[0]) > 0 { ropt.Start, err = strconv.ParseInt(sted[0], 10, 64) if err != nil { - return nil, fmt.Errorf("Invalid Parameter Range: %v,err: %v", opt.Range, err) + return nil, fmt.Errorf("Invalid Parameter Range: %v,err: %v", rangeStr, err) } ropt.HasStart = true } @@ -348,7 +351,7 @@ func GetRangeOptions(opt *ObjectGetOptions) (*RangeOptions, error) { if len(sted[1]) > 0 { ropt.End, err = strconv.ParseInt(sted[1], 10, 64) if err != nil || ropt.End == 0 { - return nil, fmt.Errorf("Invalid Parameter Range: %v,err: %v", opt.Range, err) + return nil, fmt.Errorf("Invalid Parameter Range: %v,err: %v", rangeStr, err) } ropt.HasEnd = true } diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/object.go b/vendor/github.com/tencentyun/cos-go-sdk-v5/object.go index 6e7d9f5455..9645458351 100644 --- a/vendor/github.com/tencentyun/cos-go-sdk-v5/object.go +++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/object.go @@ -1,6 +1,7 @@ package cos import ( + "bytes" "context" "crypto/md5" "encoding/hex" @@ -114,23 +115,34 @@ func (s *ObjectService) GetObjectURL(name string) *url.URL { } type PresignedURLOptions struct { - Query *url.Values `xml:"-" url:"-" header:"-"` - Header *http.Header `header:"-,omitempty" url:"-" xml:"-"` + Query *url.Values `xml:"-" url:"-" header:"-"` + Header *http.Header `header:"-,omitempty" url:"-" xml:"-"` + SignMerged bool `xml:"-" url:"-" header:"-"` } // GetPresignedURL get the object presigned to down or upload file by url -func (s *ObjectService) GetPresignedURL(ctx context.Context, httpMethod, name, ak, sk string, expired time.Duration, opt interface{}) (*url.URL, error) { +// 预签名函数,signHost: 默认签入Header Host, 您也可以选择不签入Header Host,但可能导致请求失败或安全漏洞 +func (s *ObjectService) GetPresignedURL(ctx context.Context, httpMethod, name, ak, sk string, expired time.Duration, opt interface{}, signHost ...bool) (*url.URL, error) { + // 兼容 name 以 / 开头的情况 + if strings.HasPrefix(name, "/") { + name = encodeURIComponent("/") + encodeURIComponent(name[1:], []byte{'/'}) + } else { + name = encodeURIComponent(name, []byte{'/'}) + } + sendOpt := sendOptions{ baseURL: s.client.BaseURL.BucketURL, - uri: "/" + encodeURIComponent(name), + uri: "/" + name, method: httpMethod, optQuery: opt, optHeader: opt, } if popt, ok := opt.(*PresignedURLOptions); ok { - qs := popt.Query.Encode() - if qs != "" { - sendOpt.uri = fmt.Sprintf("%s?%s", sendOpt.uri, qs) + if popt != nil && popt.Query != nil { + qs := popt.Query.Encode() + if qs != "" { + sendOpt.uri = fmt.Sprintf("%s?%s", sendOpt.uri, qs) + } } } req, err := s.client.newRequest(ctx, sendOpt.baseURL, sendOpt.uri, sendOpt.method, sendOpt.body, sendOpt.optQuery, sendOpt.optHeader) @@ -147,7 +159,24 @@ func (s *ObjectService) GetPresignedURL(ctx context.Context, httpMethod, name, a if authTime == nil { authTime = NewAuthTime(expired) } - authorization := newAuthorization(ak, sk, req, authTime) + signedHost := true + if len(signHost) > 0 { + signedHost = signHost[0] + } + authorization := newAuthorization(ak, sk, req, authTime, signedHost) + if opt != nil { + if opt, ok := opt.(*PresignedURLOptions); ok { + if opt.SignMerged { + sign := encodeURIComponent(authorization) + if req.URL.RawQuery == "" { + req.URL.RawQuery = fmt.Sprintf("sign=%s", sign) + } else { + req.URL.RawQuery = fmt.Sprintf("%s&sign=%s", req.URL.RawQuery, sign) + } + return req.URL, nil + } + } + } sign := encodeURIComponent(authorization, []byte{'&', '='}) if req.URL.RawQuery == "" { @@ -431,6 +460,17 @@ func (s *ObjectService) Head(ctx context.Context, name string, opt *ObjectHeadOp return resp, err } +func (s *ObjectService) IsExist(ctx context.Context, name string, id ...string) (bool, error) { + _, err := s.Head(ctx, name, nil, id...) + if err == nil { + return true, nil + } + if IsNotFoundError(err) { + return false, nil + } + return false, err +} + // ObjectOptionsOptions is the option of object options type ObjectOptionsOptions struct { Origin string `url:"-" header:"Origin"` @@ -1428,3 +1468,88 @@ func (s *ObjectService) DeleteTagging(ctx context.Context, name string, id ...st resp, err := s.client.doRetry(ctx, sendOpt) return resp, err } + +type PutFetchTaskOptions struct { + Url string `json:"Url,omitempty" header:"-" xml:"-"` + Key string `json:"Key,omitempty" header:"-" xml:"-"` + MD5 string `json:"MD5,omitempty" header:"-" xml:"-"` + OnKeyExist string `json:"OnKeyExist,omitempty" header:"-" xml:"-"` + IgnoreSameKey bool `json:"IgnoreSameKey,omitempty" header:"-" xml:"-"` + SuccessCallbackUrl string `json:"SuccessCallbackUrl,omitempty" header:"-" xml:"-"` + FailureCallbackUrl string `json:"FailureCallbackUrl,omitempty" header:"-" xml:"-"` + XOptionHeader *http.Header `json:"-", xml:"-" header:"-,omitempty"` +} + +type PutFetchTaskResult struct { + Code int `json:"code,omitempty"` + Message string `json:"message,omitempty"` + RequestId string `json:"request_id,omitempty"` + Data struct { + TaskId string `json:"taskId,omitempty"` + } `json:"Data,omitempty"` +} + +type GetFetchTaskResult struct { + Code int `json:"code,omitempty"` + Message string `json:"message,omitempty"` + RequestId string `json:"request_id,omitempty"` + Data struct { + Code string `json:"code,omitempty"` + Message string `json:"msg,omitempty"` + Percent int `json:"percent,omitempty"` + Status string `json:"status,omitempty"` + } `json:"data,omitempty"` +} + +type innerFetchTaskHeader struct { + XOptionHeader *http.Header `json:"-", xml:"-" header:"-,omitempty"` +} + +func (s *ObjectService) PutFetchTask(ctx context.Context, bucket string, opt *PutFetchTaskOptions) (*PutFetchTaskResult, *Response, error) { + var buf bytes.Buffer + var res PutFetchTaskResult + if opt == nil { + opt = &PutFetchTaskOptions{} + } + header := innerFetchTaskHeader{ + XOptionHeader: &http.Header{}, + } + if opt.XOptionHeader != nil { + header.XOptionHeader = cloneHeader(opt.XOptionHeader) + } + header.XOptionHeader.Set("Content-Type", "application/json") + bs, err := json.Marshal(opt) + if err != nil { + return nil, nil, err + } + reader := bytes.NewBuffer(bs) + sendOpt := &sendOptions{ + baseURL: s.client.BaseURL.FetchURL, + uri: fmt.Sprintf("/%s/", bucket), + method: http.MethodPost, + optHeader: &header, + body: reader, + result: &buf, + } + resp, err := s.client.send(ctx, sendOpt) + if buf.Len() > 0 { + err = json.Unmarshal(buf.Bytes(), &res) + } + return &res, resp, err +} + +func (s *ObjectService) GetFetchTask(ctx context.Context, bucket string, taskid string) (*GetFetchTaskResult, *Response, error) { + var buf bytes.Buffer + var res GetFetchTaskResult + sendOpt := &sendOptions{ + baseURL: s.client.BaseURL.FetchURL, + uri: fmt.Sprintf("/%s/%s", bucket, encodeURIComponent(taskid)), + method: http.MethodGet, + result: &buf, + } + resp, err := s.client.send(ctx, sendOpt) + if buf.Len() > 0 { + err = json.Unmarshal(buf.Bytes(), &res) + } + return &res, resp, err +} diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/object_part.go b/vendor/github.com/tencentyun/cos-go-sdk-v5/object_part.go index cb0fe611c2..80eac911f1 100644 --- a/vendor/github.com/tencentyun/cos-go-sdk-v5/object_part.go +++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/object_part.go @@ -286,12 +286,12 @@ func (s *ObjectService) CopyPart(ctx context.Context, name, uploadID string, par } type ObjectListUploadsOptions struct { - Delimiter string `url:"Delimiter,omitempty"` - EncodingType string `url:"EncodingType,omitempty"` - Prefix string `url:"Prefix"` - MaxUploads int `url:"MaxUploads"` - KeyMarker string `url:"KeyMarker"` - UploadIdMarker string `url:"UploadIDMarker"` + Delimiter string `url:"delimiter,omitempty"` + EncodingType string `url:"encoding-type,omitempty"` + Prefix string `url:"prefix,omitempty"` + MaxUploads int `url:"max-uploads,omitempty"` + KeyMarker string `url:"key-marker,omitempty"` + UploadIdMarker string `url:"upload-id-marker,omitempty"` } type ObjectListUploadsResult struct { @@ -336,6 +336,7 @@ type MultiCopyOptions struct { OptCopy *ObjectCopyOptions PartSize int64 ThreadPoolSize int + useMulti bool // use for ut } type CopyJobs struct { @@ -386,7 +387,7 @@ func (s *ObjectService) innerHead(ctx context.Context, sourceURL string, opt *Ob return } - u, err := url.Parse(fmt.Sprintf("https://%s", surl[0])) + u, err := url.Parse(fmt.Sprintf("http://%s", surl[0])) if err != nil { return } @@ -429,7 +430,7 @@ func (s *ObjectService) MultiCopy(ctx context.Context, name string, sourceURL st if err != nil { return nil, nil, err } - if partNum == 0 || totalBytes < singleUploadMaxLength { + if partNum == 0 || (totalBytes < singleUploadMaxLength && !opt.useMulti) { if len(id) > 0 { return s.Copy(ctx, name, sourceURL, opt.OptCopy, id[0]) } else { diff --git a/vendor/modules.txt b/vendor/modules.txt index 6a04b88d07..f1575254e1 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -613,7 +613,7 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vod/v20180717 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312 # github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/wss v1.0.199 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/wss/v20180426 -# github.com/tencentyun/cos-go-sdk-v5 v0.7.31-0.20210902132439-360bc9b1be6b +# github.com/tencentyun/cos-go-sdk-v5 v0.7.33 github.com/tencentyun/cos-go-sdk-v5 # github.com/tetafro/godot v0.3.7 github.com/tetafro/godot diff --git a/website/docs/r/cos_bucket.html.markdown b/website/docs/r/cos_bucket.html.markdown index 56f80a7b45..0278ad856b 100644 --- a/website/docs/r/cos_bucket.html.markdown +++ b/website/docs/r/cos_bucket.html.markdown @@ -211,6 +211,8 @@ The following arguments are supported: * `multi_az` - (Optional, ForceNew) Indicates whether to create a bucket of multi available zone. * `origin_domain_rules` - (Optional) Bucket Origin Domain settings. * `origin_pull_rules` - (Optional) Bucket Origin-Pull settings. +* `replica_role` - (Optional) Request initiator identifier, format: `qcs::cam::uin/:uin/.`. NOTE: only `versioning_enable` is true can configure this argument. +* `replica_rules` - (Optional) List of replica rule. NOTE: only `versioning_enable` is true and `replica_role` set can configure this argument. * `tags` - (Optional) The tags of a bucket. * `versioning_enable` - (Optional) Enable bucket versioning. * `website` - (Optional) A website object(documented below). @@ -252,6 +254,14 @@ The `origin_pull_rules` object supports the following: * `protocol` - (Optional) the protocol used for COS to access the specified origin server. The available value include `HTTP`, `HTTPS` and `FOLLOW`. * `sync_back_to_source` - (Optional) If `true`, COS will not return 3XX status code when pulling data from an origin server. Current available zone: ap-beijing, ap-shanghai, ap-singapore, ap-mumbai. +The `replica_rules` object supports the following: + +* `destination_bucket` - (Required) Destination bucket identifier, format: `qcs::cos:::`. NOTE: destination bucket must enable versioning. +* `status` - (Required) Status identifier, available values: `Enabled`, `Disabled`. +* `destination_storage_class` - (Optional) Storage class of destination, available values: `STANDARD`, `INTELLIGENT_TIERING`, `STANDARD_IA`. default is following current class of destination. +* `id` - (Optional) Name of a specific rule. +* `prefix` - (Optional) Prefix matching policy. Policies cannot overlap; otherwise, an error will be returned. To match the root directory, leave this parameter empty. + The `transition` object supports the following: * `storage_class` - (Required) Specifies the storage class to which you want the object to transition. Available values include `STANDARD`, `STANDARD_IA` and `ARCHIVE`. From 35ba82a00ad459c56b835089344769bcb07f7c22 Mon Sep 17 00:00:00 2001 From: KGSN Date: Fri, 7 Jan 2022 18:02:20 +0800 Subject: [PATCH 2/2] cos - add replica examples --- tencentcloud/resource_tc_cos_bucket.go | 24 +++++++++++++++++++++++- website/docs/r/cos_bucket.html.markdown | 25 ++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/tencentcloud/resource_tc_cos_bucket.go b/tencentcloud/resource_tc_cos_bucket.go index 1064518b28..f916eb2e75 100644 --- a/tencentcloud/resource_tc_cos_bucket.go +++ b/tencentcloud/resource_tc_cos_bucket.go @@ -133,6 +133,28 @@ resource "tencentcloud_cos_bucket" "with_origin" { } ``` +Using replication +```hcl +resource "tencentcloud_cos_bucket" "replica1" { + bucket = "tf-replica-foo-1234567890" + acl = "private" + versioning_enable = true +} + +resource "tencentcloud_cos_bucket" "with_replication" { + bucket = "tf-bucket-replica-1234567890" + acl = "private" + versioning_enable = true + replica_role = "qcs::cam::uin/100000000001:uin/100000000001" + replica_rules { + id = "test-rep1" + status = "Enabled" + prefix = "dist" + destination_bucket = "qcs::cos:%s::${tencentcloud_cos_bucket.replica1.bucket}" + } +} +``` + Setting log status ```hcl @@ -389,7 +411,7 @@ func resourceTencentCloudCosBucket() *schema.Resource { Type: schema.TypeString, Optional: true, RequiredWith: []string{"replica_rules", "versioning_enable"}, - Description: "Request initiator identifier, format: `qcs::cam::uin/:uin/.`. NOTE: only `versioning_enable` is true can configure this argument.", + Description: "Request initiator identifier, format: `qcs::cam::uin/:uin/`. NOTE: only `versioning_enable` is true can configure this argument.", }, "replica_rules": { Type: schema.TypeList, diff --git a/website/docs/r/cos_bucket.html.markdown b/website/docs/r/cos_bucket.html.markdown index 0278ad856b..405a2b339c 100644 --- a/website/docs/r/cos_bucket.html.markdown +++ b/website/docs/r/cos_bucket.html.markdown @@ -145,6 +145,29 @@ resource "tencentcloud_cos_bucket" "with_origin" { } ``` +Using replication + +```hcl +resource "tencentcloud_cos_bucket" "replica1" { + bucket = "tf-replica-foo-1234567890" + acl = "private" + versioning_enable = true +} + +resource "tencentcloud_cos_bucket" "with_replication" { + bucket = "tf-bucket-replica-1234567890" + acl = "private" + versioning_enable = true + replica_role = "qcs::cam::uin/100000000001:uin/100000000001" + replica_rules { + id = "test-rep1" + status = "Enabled" + prefix = "dist" + destination_bucket = "qcs::cos:%s::${tencentcloud_cos_bucket.replica1.bucket}" + } +} +``` + Setting log status ```hcl @@ -211,7 +234,7 @@ The following arguments are supported: * `multi_az` - (Optional, ForceNew) Indicates whether to create a bucket of multi available zone. * `origin_domain_rules` - (Optional) Bucket Origin Domain settings. * `origin_pull_rules` - (Optional) Bucket Origin-Pull settings. -* `replica_role` - (Optional) Request initiator identifier, format: `qcs::cam::uin/:uin/.`. NOTE: only `versioning_enable` is true can configure this argument. +* `replica_role` - (Optional) Request initiator identifier, format: `qcs::cam::uin/:uin/`. NOTE: only `versioning_enable` is true can configure this argument. * `replica_rules` - (Optional) List of replica rule. NOTE: only `versioning_enable` is true and `replica_role` set can configure this argument. * `tags` - (Optional) The tags of a bucket. * `versioning_enable` - (Optional) Enable bucket versioning.