diff --git a/CODEOWNERS b/CODEOWNERS
index cbc814bea315415f8643eb843efb38ece6a9523b..52407879753b039d8b365f294fb17b231989d9de 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -5,3 +5,4 @@
 /backend/remote-state/azure     @hashicorp/terraform-azure
 /backend/remote-state/gcs       @hashicorp/terraform-google
 /backend/remote-state/s3        @hashicorp/terraform-aws
+/backend/remote-state/s3        @likexian
\ No newline at end of file
diff --git a/backend/init/init.go b/backend/init/init.go
index 5142c41935d0eff8dfda86e7e5c9f3ede02519af..d4a239791f9324cc454b831fd83e9f55b25b90e8 100644
--- a/backend/init/init.go
+++ b/backend/init/init.go
@@ -16,6 +16,7 @@ import (
 	backendArtifactory "github.com/hashicorp/terraform/backend/remote-state/artifactory"
 	backendAzure "github.com/hashicorp/terraform/backend/remote-state/azure"
 	backendConsul "github.com/hashicorp/terraform/backend/remote-state/consul"
+	backendCos "github.com/hashicorp/terraform/backend/remote-state/cos"
 	backendEtcdv2 "github.com/hashicorp/terraform/backend/remote-state/etcdv2"
 	backendEtcdv3 "github.com/hashicorp/terraform/backend/remote-state/etcdv3"
 	backendGCS "github.com/hashicorp/terraform/backend/remote-state/gcs"
@@ -57,6 +58,7 @@ func Init(services *disco.Disco) {
 		"atlas":       func() backend.Backend { return backendAtlas.New() },
 		"azurerm":     func() backend.Backend { return backendAzure.New() },
 		"consul":      func() backend.Backend { return backendConsul.New() },
+		"cos":         func() backend.Backend { return backendCos.New() },
 		"etcd":        func() backend.Backend { return backendEtcdv2.New() },
 		"etcdv3":      func() backend.Backend { return backendEtcdv3.New() },
 		"gcs":         func() backend.Backend { return backendGCS.New() },
diff --git a/backend/init/init_test.go b/backend/init/init_test.go
index 74dbf53aa2f8be9a6bca3b8412795d9efd548439..2b7571b54442ea6be30888a349f77dc64eeb4c1d 100644
--- a/backend/init/init_test.go
+++ b/backend/init/init_test.go
@@ -18,6 +18,7 @@ func TestInit_backend(t *testing.T) {
 		{"atlas", "*atlas.Backend"},
 		{"azurerm", "*azure.Backend"},
 		{"consul", "*consul.Backend"},
+		{"cos", "*cos.Backend"},
 		{"etcdv3", "*etcd.Backend"},
 		{"gcs", "*gcs.Backend"},
 		{"inmem", "*inmem.Backend"},
diff --git a/backend/remote-state/cos/backend.go b/backend/remote-state/cos/backend.go
new file mode 100644
index 0000000000000000000000000000000000000000..ce502e5cc8762374090c085bceceab7c091b2bf9
--- /dev/null
+++ b/backend/remote-state/cos/backend.go
@@ -0,0 +1,169 @@
+package cos
+
+import (
+	"context"
+	"fmt"
+	"net/http"
+	"net/url"
+	"strings"
+	"time"
+
+	"github.com/hashicorp/terraform/backend"
+	"github.com/hashicorp/terraform/helper/schema"
+	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
+	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
+	tag "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tag/v20180813"
+	"github.com/tencentyun/cos-go-sdk-v5"
+)
+
+// Default value from environment variable
+const (
+	PROVIDER_SECRET_ID  = "TENCENTCLOUD_SECRET_ID"
+	PROVIDER_SECRET_KEY = "TENCENTCLOUD_SECRET_KEY"
+	PROVIDER_REGION     = "TENCENTCLOUD_REGION"
+)
+
+// Backend implements "backend".Backend for tencentCloud cos
+type Backend struct {
+	*schema.Backend
+
+	cosContext context.Context
+	cosClient  *cos.Client
+	tagClient  *tag.Client
+
+	region  string
+	bucket  string
+	prefix  string
+	key     string
+	encrypt bool
+	acl     string
+}
+
+// New creates a new backend for TencentCloud cos remote state.
+func New() backend.Backend {
+	s := &schema.Backend{
+		Schema: map[string]*schema.Schema{
+			"secret_id": {
+				Type:        schema.TypeString,
+				Required:    true,
+				DefaultFunc: schema.EnvDefaultFunc(PROVIDER_SECRET_ID, nil),
+				Description: "Secret id of Tencent Cloud",
+			},
+			"secret_key": {
+				Type:        schema.TypeString,
+				Required:    true,
+				DefaultFunc: schema.EnvDefaultFunc(PROVIDER_SECRET_KEY, nil),
+				Description: "Secret key of Tencent Cloud",
+				Sensitive:   true,
+			},
+			"region": {
+				Type:         schema.TypeString,
+				Required:     true,
+				DefaultFunc:  schema.EnvDefaultFunc(PROVIDER_REGION, nil),
+				Description:  "The region of the COS bucket",
+				InputDefault: "ap-guangzhou",
+			},
+			"bucket": {
+				Type:        schema.TypeString,
+				Required:    true,
+				Description: "The name of the COS bucket",
+			},
+			"prefix": {
+				Type:        schema.TypeString,
+				Optional:    true,
+				Description: "The directory for saving the state file in bucket",
+				ValidateFunc: func(v interface{}, s string) ([]string, []error) {
+					prefix := v.(string)
+					if strings.HasPrefix(prefix, "/") || strings.HasPrefix(prefix, "./") {
+						return nil, []error{fmt.Errorf("prefix must not start with '/' or './'")}
+					}
+					return nil, nil
+				},
+			},
+			"key": {
+				Type:        schema.TypeString,
+				Optional:    true,
+				Description: "The path for saving the state file in bucket",
+				Default:     "terraform.tfstate",
+				ValidateFunc: func(v interface{}, s string) ([]string, []error) {
+					if strings.HasPrefix(v.(string), "/") || strings.HasSuffix(v.(string), "/") {
+						return nil, []error{fmt.Errorf("key can not start and end with '/'")}
+					}
+					return nil, nil
+				},
+			},
+			"encrypt": {
+				Type:        schema.TypeBool,
+				Optional:    true,
+				Description: "Whether to enable server side encryption of the state file",
+				Default:     true,
+			},
+			"acl": {
+				Type:        schema.TypeString,
+				Optional:    true,
+				Description: "Object ACL to be applied to the state file",
+				Default:     "private",
+				ValidateFunc: func(v interface{}, s string) ([]string, []error) {
+					value := v.(string)
+					if value != "private" && value != "public-read" {
+						return nil, []error{fmt.Errorf(
+							"acl value invalid, expected %s or %s, got %s",
+							"private", "public-read", value)}
+					}
+					return nil, nil
+				},
+			},
+		},
+	}
+
+	result := &Backend{Backend: s}
+	result.Backend.ConfigureFunc = result.configure
+
+	return result
+}
+
+// configure init cos client
+func (b *Backend) configure(ctx context.Context) error {
+	if b.cosClient != nil {
+		return nil
+	}
+
+	b.cosContext = ctx
+	data := schema.FromContextBackendConfig(b.cosContext)
+
+	b.region = data.Get("region").(string)
+	b.bucket = data.Get("bucket").(string)
+	b.prefix = data.Get("prefix").(string)
+	b.key = data.Get("key").(string)
+	b.encrypt = data.Get("encrypt").(bool)
+	b.acl = data.Get("acl").(string)
+
+	u, err := url.Parse(fmt.Sprintf("https://%s.cos.%s.myqcloud.com", b.bucket, b.region))
+	if err != nil {
+		return err
+	}
+
+	b.cosClient = cos.NewClient(
+		&cos.BaseURL{BucketURL: u},
+		&http.Client{
+			Timeout: 60 * time.Second,
+			Transport: &cos.AuthorizationTransport{
+				SecretID:  data.Get("secret_id").(string),
+				SecretKey: data.Get("secret_key").(string),
+			},
+		},
+	)
+
+	credential := common.NewCredential(
+		data.Get("secret_id").(string),
+		data.Get("secret_key").(string),
+	)
+
+	cpf := profile.NewClientProfile()
+	cpf.HttpProfile.ReqMethod = "POST"
+	cpf.HttpProfile.ReqTimeout = 300
+	cpf.Language = "en-US"
+	b.tagClient, err = tag.NewClient(credential, b.region, cpf)
+
+	return err
+}
diff --git a/backend/remote-state/cos/backend_state.go b/backend/remote-state/cos/backend_state.go
new file mode 100644
index 0000000000000000000000000000000000000000..2bc3f242807dffc50a21d2f594e66e5d818f7c5c
--- /dev/null
+++ b/backend/remote-state/cos/backend_state.go
@@ -0,0 +1,178 @@
+package cos
+
+import (
+	"fmt"
+	"log"
+	"path"
+	"sort"
+	"strings"
+
+	"github.com/hashicorp/terraform/backend"
+	"github.com/hashicorp/terraform/state"
+	"github.com/hashicorp/terraform/state/remote"
+	"github.com/hashicorp/terraform/states"
+	"github.com/likexian/gokit/assert"
+)
+
+// Define file suffix
+const (
+	stateFileSuffix = ".tfstate"
+	lockFileSuffix  = ".tflock"
+)
+
+// Workspaces returns a list of names for the workspaces
+func (b *Backend) Workspaces() ([]string, error) {
+	c, err := b.client("tencentcloud")
+	if err != nil {
+		return nil, err
+	}
+
+	obs, err := c.getBucket(b.prefix)
+	log.Printf("[DEBUG] list all workspaces, objects: %v, error: %v", obs, err)
+	if err != nil {
+		return nil, err
+	}
+
+	ws := []string{backend.DefaultStateName}
+	for _, vv := range obs {
+		// <name>.tfstate
+		if !strings.HasSuffix(vv.Key, stateFileSuffix) {
+			continue
+		}
+		// default worksapce
+		if path.Join(b.prefix, b.key) == vv.Key {
+			continue
+		}
+		// <prefix>/<worksapce>/<key>
+		prefix := strings.TrimRight(b.prefix, "/") + "/"
+		parts := strings.Split(strings.TrimPrefix(vv.Key, prefix), "/")
+		if len(parts) > 0 && parts[0] != "" {
+			ws = append(ws, parts[0])
+		}
+	}
+
+	sort.Strings(ws[1:])
+	log.Printf("[DEBUG] list all workspaces, workspaces: %v", ws)
+
+	return ws, nil
+}
+
+// DeleteWorkspace deletes the named workspaces. The "default" state cannot be deleted.
+func (b *Backend) DeleteWorkspace(name string) error {
+	log.Printf("[DEBUG] delete workspace, workspace: %v", name)
+
+	if name == backend.DefaultStateName || name == "" {
+		return fmt.Errorf("default state is not allow to delete")
+	}
+
+	c, err := b.client(name)
+	if err != nil {
+		return err
+	}
+
+	return c.Delete()
+}
+
+// StateMgr manage the state, if the named state not exists, a new file will created
+func (b *Backend) StateMgr(name string) (state.State, error) {
+	log.Printf("[DEBUG] state manager, current workspace: %v", name)
+
+	c, err := b.client(name)
+	if err != nil {
+		return nil, err
+	}
+	stateMgr := &remote.State{Client: c}
+
+	ws, err := b.Workspaces()
+	if err != nil {
+		return nil, err
+	}
+
+	if !assert.IsContains(ws, name) {
+		log.Printf("[DEBUG] workspace %v not exists", name)
+
+		// take a lock on this state while we write it
+		lockInfo := state.NewLockInfo()
+		lockInfo.Operation = "init"
+		lockId, err := c.Lock(lockInfo)
+		if err != nil {
+			return nil, fmt.Errorf("Failed to lock cos state: %s", err)
+		}
+
+		// Local helper function so we can call it multiple places
+		lockUnlock := func(e error) error {
+			if err := stateMgr.Unlock(lockId); err != nil {
+				return fmt.Errorf(unlockErrMsg, err, lockId)
+			}
+			return e
+		}
+
+		// Grab the value
+		if err := stateMgr.RefreshState(); err != nil {
+			err = lockUnlock(err)
+			return nil, err
+		}
+
+		// If we have no state, we have to create an empty state
+		if v := stateMgr.State(); v == nil {
+			if err := stateMgr.WriteState(states.NewState()); err != nil {
+				err = lockUnlock(err)
+				return nil, err
+			}
+			if err := stateMgr.PersistState(); err != nil {
+				err = lockUnlock(err)
+				return nil, err
+			}
+		}
+
+		// Unlock, the state should now be initialized
+		if err := lockUnlock(nil); err != nil {
+			return nil, err
+		}
+	}
+
+	return stateMgr, nil
+}
+
+// client returns a remoteClient for the named state.
+func (b *Backend) client(name string) (*remoteClient, error) {
+	if strings.TrimSpace(name) == "" {
+		return nil, fmt.Errorf("state name not allow to be empty")
+	}
+
+	return &remoteClient{
+		cosContext: b.cosContext,
+		cosClient:  b.cosClient,
+		tagClient:  b.tagClient,
+		bucket:     b.bucket,
+		stateFile:  b.stateFile(name),
+		lockFile:   b.lockFile(name),
+		encrypt:    b.encrypt,
+		acl:        b.acl,
+	}, nil
+}
+
+// stateFile returns state file path by name
+func (b *Backend) stateFile(name string) string {
+	if name == backend.DefaultStateName {
+		return path.Join(b.prefix, b.key)
+	}
+	return path.Join(b.prefix, name, b.key)
+}
+
+// lockFile returns lock file path by name
+func (b *Backend) lockFile(name string) string {
+	return b.stateFile(name) + lockFileSuffix
+}
+
+// unlockErrMsg is error msg for unlock failed
+const unlockErrMsg = `
+Unlocking the state file on TencentCloud cos backend failed:
+
+Error message: %v
+Lock ID (gen): %s
+
+You may have to force-unlock this state in order to use it again.
+The TencentCloud backend acquires a lock during initialization
+to ensure the initial state file is created.
+`
diff --git a/backend/remote-state/cos/backend_test.go b/backend/remote-state/cos/backend_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..b372571af19067063847fd83fcf5d2acf2600b34
--- /dev/null
+++ b/backend/remote-state/cos/backend_test.go
@@ -0,0 +1,227 @@
+package cos
+
+import (
+	"crypto/md5"
+	"fmt"
+	"os"
+	"testing"
+	"time"
+
+	"github.com/hashicorp/terraform/backend"
+	"github.com/hashicorp/terraform/state/remote"
+	"github.com/likexian/gokit/assert"
+)
+
+const (
+	defaultPrefix = ""
+	defaultKey    = "terraform.tfstate"
+)
+
+// Testing Thanks to GCS
+
+func TestStateFile(t *testing.T) {
+	t.Parallel()
+
+	cases := []struct {
+		prefix        string
+		stateName     string
+		key           string
+		wantStateFile string
+		wantLockFile  string
+	}{
+		{"", "default", "default.tfstate", "default.tfstate", "default.tfstate.tflock"},
+		{"", "default", "test.tfstate", "test.tfstate", "test.tfstate.tflock"},
+		{"", "dev", "test.tfstate", "dev/test.tfstate", "dev/test.tfstate.tflock"},
+		{"terraform/test", "default", "default.tfstate", "terraform/test/default.tfstate", "terraform/test/default.tfstate.tflock"},
+		{"terraform/test", "default", "test.tfstate", "terraform/test/test.tfstate", "terraform/test/test.tfstate.tflock"},
+		{"terraform/test", "dev", "test.tfstate", "terraform/test/dev/test.tfstate", "terraform/test/dev/test.tfstate.tflock"},
+	}
+
+	for _, c := range cases {
+		b := &Backend{
+			prefix: c.prefix,
+			key:    c.key,
+		}
+		assert.Equal(t, b.stateFile(c.stateName), c.wantStateFile)
+		assert.Equal(t, b.lockFile(c.stateName), c.wantLockFile)
+	}
+}
+
+func TestRemoteClient(t *testing.T) {
+	t.Parallel()
+
+	bucket := bucketName(t)
+
+	be := setupBackend(t, bucket, defaultPrefix, defaultKey, false)
+	defer teardownBackend(t, be)
+
+	ss, err := be.StateMgr(backend.DefaultStateName)
+	assert.Nil(t, err)
+
+	rs, ok := ss.(*remote.State)
+	assert.True(t, ok)
+
+	remote.TestClient(t, rs.Client)
+}
+
+func TestRemoteClientWithPrefix(t *testing.T) {
+	t.Parallel()
+
+	prefix := "prefix/test"
+	bucket := bucketName(t)
+
+	be := setupBackend(t, bucket, prefix, defaultKey, false)
+	defer teardownBackend(t, be)
+
+	ss, err := be.StateMgr(backend.DefaultStateName)
+	assert.Nil(t, err)
+
+	rs, ok := ss.(*remote.State)
+	assert.True(t, ok)
+
+	remote.TestClient(t, rs.Client)
+}
+
+func TestRemoteClientWithEncryption(t *testing.T) {
+	t.Parallel()
+
+	bucket := bucketName(t)
+
+	be := setupBackend(t, bucket, defaultPrefix, defaultKey, true)
+	defer teardownBackend(t, be)
+
+	ss, err := be.StateMgr(backend.DefaultStateName)
+	assert.Nil(t, err)
+
+	rs, ok := ss.(*remote.State)
+	assert.True(t, ok)
+
+	remote.TestClient(t, rs.Client)
+}
+
+func TestRemoteLocks(t *testing.T) {
+	t.Parallel()
+
+	bucket := bucketName(t)
+
+	be := setupBackend(t, bucket, defaultPrefix, defaultKey, false)
+	defer teardownBackend(t, be)
+
+	remoteClient := func() (remote.Client, error) {
+		ss, err := be.StateMgr(backend.DefaultStateName)
+		if err != nil {
+			return nil, err
+		}
+
+		rs, ok := ss.(*remote.State)
+		if !ok {
+			return nil, fmt.Errorf("be.StateMgr(): got a %T, want a *remote.State", ss)
+		}
+
+		return rs.Client, nil
+	}
+
+	c0, err := remoteClient()
+	assert.Nil(t, err)
+
+	c1, err := remoteClient()
+	assert.Nil(t, err)
+
+	remote.TestRemoteLocks(t, c0, c1)
+}
+
+func TestBackend(t *testing.T) {
+	t.Parallel()
+
+	bucket := bucketName(t)
+
+	be0 := setupBackend(t, bucket, defaultPrefix, defaultKey, false)
+	defer teardownBackend(t, be0)
+
+	be1 := setupBackend(t, bucket, defaultPrefix, defaultKey, false)
+	defer teardownBackend(t, be1)
+
+	backend.TestBackendStates(t, be0)
+	backend.TestBackendStateLocks(t, be0, be1)
+	backend.TestBackendStateForceUnlock(t, be0, be1)
+}
+
+func TestBackendWithPrefix(t *testing.T) {
+	t.Parallel()
+
+	prefix := "prefix/test"
+	bucket := bucketName(t)
+
+	be0 := setupBackend(t, bucket, prefix, defaultKey, false)
+	defer teardownBackend(t, be0)
+
+	be1 := setupBackend(t, bucket, prefix+"/", defaultKey, false)
+	defer teardownBackend(t, be1)
+
+	backend.TestBackendStates(t, be0)
+	backend.TestBackendStateLocks(t, be0, be1)
+}
+
+func TestBackendWithEncryption(t *testing.T) {
+	t.Parallel()
+
+	bucket := bucketName(t)
+
+	be0 := setupBackend(t, bucket, defaultPrefix, defaultKey, true)
+	defer teardownBackend(t, be0)
+
+	be1 := setupBackend(t, bucket, defaultPrefix, defaultKey, true)
+	defer teardownBackend(t, be1)
+
+	backend.TestBackendStates(t, be0)
+	backend.TestBackendStateLocks(t, be0, be1)
+}
+
+func setupBackend(t *testing.T, bucket, prefix, key string, encrypt bool) backend.Backend {
+	t.Helper()
+
+	skip := os.Getenv("TF_COS_APPID") == ""
+	if skip {
+		t.Skip("This test require setting TF_COS_APPID environment variables")
+	}
+
+	if os.Getenv(PROVIDER_REGION) == "" {
+		os.Setenv(PROVIDER_REGION, "ap-guangzhou")
+	}
+
+	appId := os.Getenv("TF_COS_APPID")
+	region := os.Getenv(PROVIDER_REGION)
+
+	config := map[string]interface{}{
+		"region": region,
+		"bucket": bucket + appId,
+		"prefix": prefix,
+		"key":    key,
+	}
+
+	b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(config))
+	be := b.(*Backend)
+
+	c, err := be.client("tencentcloud")
+	assert.Nil(t, err)
+
+	err = c.putBucket()
+	assert.Nil(t, err)
+
+	return b
+}
+
+func teardownBackend(t *testing.T, b backend.Backend) {
+	t.Helper()
+
+	c, err := b.(*Backend).client("tencentcloud")
+	assert.Nil(t, err)
+
+	err = c.deleteBucket(true)
+	assert.Nil(t, err)
+}
+
+func bucketName(t *testing.T) string {
+	unique := fmt.Sprintf("%s-%x", t.Name(), time.Now().UnixNano())
+	return fmt.Sprintf("terraform-test-%s-%s", fmt.Sprintf("%x", md5.Sum([]byte(unique)))[:10], "")
+}
diff --git a/backend/remote-state/cos/client.go b/backend/remote-state/cos/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..281fd2b7a7f5d322b9d21f1acd4474f2d6a6586b
--- /dev/null
+++ b/backend/remote-state/cos/client.go
@@ -0,0 +1,403 @@
+package cos
+
+import (
+	"bytes"
+	"context"
+	"crypto/md5"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"log"
+	"net/http"
+	"strings"
+	"time"
+
+	multierror "github.com/hashicorp/go-multierror"
+	"github.com/hashicorp/terraform/state"
+	"github.com/hashicorp/terraform/state/remote"
+	tag "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tag/v20180813"
+	"github.com/tencentyun/cos-go-sdk-v5"
+)
+
+const (
+	lockTagKey = "tencentcloud-terraform-lock"
+)
+
+// RemoteClient implements the client of remote state
+type remoteClient struct {
+	cosContext context.Context
+	cosClient  *cos.Client
+	tagClient  *tag.Client
+
+	bucket    string
+	stateFile string
+	lockFile  string
+	encrypt   bool
+	acl       string
+}
+
+// Get returns remote state file
+func (c *remoteClient) Get() (*remote.Payload, error) {
+	log.Printf("[DEBUG] get remote state file %s", c.stateFile)
+
+	exists, data, checksum, err := c.getObject(c.stateFile)
+	if err != nil {
+		return nil, err
+	}
+
+	if !exists {
+		return nil, nil
+	}
+
+	payload := &remote.Payload{
+		Data: data,
+		MD5:  []byte(checksum),
+	}
+
+	return payload, nil
+}
+
+// Put put state file to remote
+func (c *remoteClient) Put(data []byte) error {
+	log.Printf("[DEBUG] put remote state file %s", c.stateFile)
+
+	return c.putObject(c.stateFile, data)
+}
+
+// Delete delete remote state file
+func (c *remoteClient) Delete() error {
+	log.Printf("[DEBUG] delete remote state file %s", c.stateFile)
+
+	return c.deleteObject(c.stateFile)
+}
+
+// Lock lock remote state file for writing
+func (c *remoteClient) Lock(info *state.LockInfo) (string, error) {
+	log.Printf("[DEBUG] lock remote state file %s", c.lockFile)
+
+	err := c.cosLock(c.bucket, c.lockFile)
+	if err != nil {
+		return "", c.lockError(err)
+	}
+	defer c.cosUnlock(c.bucket, c.lockFile)
+
+	exists, _, _, err := c.getObject(c.lockFile)
+	if err != nil {
+		return "", c.lockError(err)
+	}
+
+	if exists {
+		return "", c.lockError(fmt.Errorf("lock file %s exists", c.lockFile))
+	}
+
+	info.Path = c.lockFile
+	data, err := json.Marshal(info)
+	if err != nil {
+		return "", c.lockError(err)
+	}
+
+	check := fmt.Sprintf("%x", md5.Sum(data))
+	err = c.putObject(c.lockFile, data)
+	if err != nil {
+		return "", c.lockError(err)
+	}
+
+	return check, nil
+}
+
+// Unlock unlock remote state file
+func (c *remoteClient) Unlock(check string) error {
+	log.Printf("[DEBUG] unlock remote state file %s", c.lockFile)
+
+	info, err := c.lockInfo()
+	if err != nil {
+		return c.lockError(err)
+	}
+
+	if info.ID != check {
+		return c.lockError(fmt.Errorf("lock id mismatch, %v != %v", info.ID, check))
+	}
+
+	err = c.deleteObject(c.lockFile)
+	if err != nil {
+		return c.lockError(err)
+	}
+
+	return nil
+}
+
+// lockError returns state.LockError
+func (c *remoteClient) lockError(err error) *state.LockError {
+	log.Printf("[DEBUG] failed to lock or unlock %s: %v", c.lockFile, err)
+
+	lockErr := &state.LockError{
+		Err: err,
+	}
+
+	info, infoErr := c.lockInfo()
+	if infoErr != nil {
+		lockErr.Err = multierror.Append(lockErr.Err, infoErr)
+	} else {
+		lockErr.Info = info
+	}
+
+	return lockErr
+}
+
+// lockInfo returns LockInfo from lock file
+func (c *remoteClient) lockInfo() (*state.LockInfo, error) {
+	exists, data, checksum, err := c.getObject(c.lockFile)
+	if err != nil {
+		return nil, err
+	}
+
+	if !exists {
+		return nil, fmt.Errorf("lock file %s not exists", c.lockFile)
+	}
+
+	info := &state.LockInfo{}
+	if err := json.Unmarshal(data, info); err != nil {
+		return nil, err
+	}
+
+	info.ID = checksum
+
+	return info, nil
+}
+
+// getObject get remote object
+func (c *remoteClient) getObject(cosFile string) (exists bool, data []byte, checksum string, err error) {
+	rsp, err := c.cosClient.Object.Get(c.cosContext, cosFile, nil)
+	if rsp == nil {
+		log.Printf("[DEBUG] getObject %s: error: %v", cosFile, err)
+		err = fmt.Errorf("failed to open file at %v: %v", cosFile, err)
+		return
+	}
+	defer rsp.Body.Close()
+
+	log.Printf("[DEBUG] getObject %s: code: %d, error: %v", cosFile, rsp.StatusCode, err)
+	if err != nil {
+		if rsp.StatusCode == 404 {
+			err = nil
+		} else {
+			err = fmt.Errorf("failed to open file at %v: %v", cosFile, err)
+		}
+		return
+	}
+
+	checksum = rsp.Header.Get("X-Cos-Meta-Md5")
+	log.Printf("[DEBUG] getObject %s: checksum: %s", cosFile, checksum)
+	if len(checksum) != 32 {
+		err = fmt.Errorf("failed to open file at %v: checksum %s invalid", cosFile, checksum)
+		return
+	}
+
+	exists = true
+	data, err = ioutil.ReadAll(rsp.Body)
+	log.Printf("[DEBUG] getObject %s: data length: %d", cosFile, len(data))
+	if err != nil {
+		err = fmt.Errorf("failed to open file at %v: %v", cosFile, err)
+		return
+	}
+
+	check := fmt.Sprintf("%x", md5.Sum(data))
+	log.Printf("[DEBUG] getObject %s: check: %s", cosFile, check)
+	if check != checksum {
+		err = fmt.Errorf("failed to open file at %v: checksum mismatch, %s != %s", cosFile, check, checksum)
+		return
+	}
+
+	return
+}
+
+// putObject put object to remote
+func (c *remoteClient) putObject(cosFile string, data []byte) error {
+	opt := &cos.ObjectPutOptions{
+		ObjectPutHeaderOptions: &cos.ObjectPutHeaderOptions{
+			XCosMetaXXX: &http.Header{
+				"X-Cos-Meta-Md5": []string{fmt.Sprintf("%x", md5.Sum(data))},
+			},
+		},
+		ACLHeaderOptions: &cos.ACLHeaderOptions{
+			XCosACL: c.acl,
+		},
+	}
+
+	if c.encrypt {
+		opt.ObjectPutHeaderOptions.XCosServerSideEncryption = "AES256"
+	}
+
+	r := bytes.NewReader(data)
+	rsp, err := c.cosClient.Object.Put(c.cosContext, cosFile, r, opt)
+	if rsp == nil {
+		log.Printf("[DEBUG] putObject %s: error: %v", cosFile, err)
+		return fmt.Errorf("failed to save file to %v: %v", cosFile, err)
+	}
+	defer rsp.Body.Close()
+
+	log.Printf("[DEBUG] putObject %s: code: %d, error: %v", cosFile, rsp.StatusCode, err)
+	if err != nil {
+		return fmt.Errorf("failed to save file to %v: %v", cosFile, err)
+	}
+
+	return nil
+}
+
+// deleteObject delete remote object
+func (c *remoteClient) deleteObject(cosFile string) error {
+	rsp, err := c.cosClient.Object.Delete(c.cosContext, cosFile)
+	if rsp == nil {
+		log.Printf("[DEBUG] deleteObject %s: error: %v", cosFile, err)
+		return fmt.Errorf("failed to delete file %v: %v", cosFile, err)
+	}
+	defer rsp.Body.Close()
+
+	log.Printf("[DEBUG] deleteObject %s: code: %d, error: %v", cosFile, rsp.StatusCode, err)
+	if rsp.StatusCode == 404 {
+		return nil
+	}
+
+	if err != nil {
+		return fmt.Errorf("failed to delete file %v: %v", cosFile, err)
+	}
+
+	return nil
+}
+
+// getBucket list bucket by prefix
+func (c *remoteClient) getBucket(prefix string) (obs []cos.Object, err error) {
+	fs, rsp, err := c.cosClient.Bucket.Get(c.cosContext, &cos.BucketGetOptions{Prefix: prefix})
+	if rsp == nil {
+		log.Printf("[DEBUG] getBucket %s/%s: error: %v", c.bucket, prefix, err)
+		err = fmt.Errorf("bucket %s not exists", c.bucket)
+		return
+	}
+	defer rsp.Body.Close()
+
+	log.Printf("[DEBUG] getBucket %s/%s: code: %d, error: %v", c.bucket, prefix, rsp.StatusCode, err)
+	if rsp.StatusCode == 404 {
+		err = fmt.Errorf("bucket %s not exists", c.bucket)
+		return
+	}
+
+	if err != nil {
+		return
+	}
+
+	return fs.Contents, nil
+}
+
+// putBucket create cos bucket
+func (c *remoteClient) putBucket() error {
+	rsp, err := c.cosClient.Bucket.Put(c.cosContext, nil)
+	if rsp == nil {
+		log.Printf("[DEBUG] putBucket %s: error: %v", c.bucket, err)
+		return fmt.Errorf("failed to create bucket %v: %v", c.bucket, err)
+	}
+	defer rsp.Body.Close()
+
+	log.Printf("[DEBUG] putBucket %s: code: %d, error: %v", c.bucket, rsp.StatusCode, err)
+	if rsp.StatusCode == 409 {
+		return nil
+	}
+
+	if err != nil {
+		return fmt.Errorf("failed to create bucket %v: %v", c.bucket, err)
+	}
+
+	return nil
+}
+
+// deleteBucket delete cos bucket
+func (c *remoteClient) deleteBucket(recursive bool) error {
+	if recursive {
+		obs, err := c.getBucket("")
+		if err != nil {
+			if strings.Contains(err.Error(), "not exists") {
+				return nil
+			}
+			log.Printf("[DEBUG] deleteBucket %s: empty bucket error: %v", c.bucket, err)
+			return fmt.Errorf("failed to empty bucket %v: %v", c.bucket, err)
+		}
+		for _, v := range obs {
+			c.deleteObject(v.Key)
+		}
+	}
+
+	rsp, err := c.cosClient.Bucket.Delete(c.cosContext)
+	if rsp == nil {
+		log.Printf("[DEBUG] deleteBucket %s: error: %v", c.bucket, err)
+		return fmt.Errorf("failed to delete bucket %v: %v", c.bucket, err)
+	}
+	defer rsp.Body.Close()
+
+	log.Printf("[DEBUG] deleteBucket %s: code: %d, error: %v", c.bucket, rsp.StatusCode, err)
+	if rsp.StatusCode == 404 {
+		return nil
+	}
+
+	if err != nil {
+		return fmt.Errorf("failed to delete bucket %v: %v", c.bucket, err)
+	}
+
+	return nil
+}
+
+// cosLock lock cos for writing
+func (c *remoteClient) cosLock(bucket, cosFile string) error {
+	log.Printf("[DEBUG] lock cos file %s:%s", bucket, cosFile)
+
+	cosPath := fmt.Sprintf("%s:%s", bucket, cosFile)
+	lockTagValue := fmt.Sprintf("%x", md5.Sum([]byte(cosPath)))
+
+	return c.CreateTag(lockTagKey, lockTagValue)
+}
+
+// cosUnlock unlock cos writing
+func (c *remoteClient) cosUnlock(bucket, cosFile string) error {
+	log.Printf("[DEBUG] unlock cos file %s:%s", bucket, cosFile)
+
+	cosPath := fmt.Sprintf("%s:%s", bucket, cosFile)
+	lockTagValue := fmt.Sprintf("%x", md5.Sum([]byte(cosPath)))
+
+	var err error
+	for i := 0; i < 30; i++ {
+		err = c.DeleteTag(lockTagKey, lockTagValue)
+		if err == nil {
+			return nil
+		}
+		time.Sleep(1 * time.Second)
+	}
+
+	return err
+}
+
+// CreateTag create tag by key and value
+func (c *remoteClient) CreateTag(key, value string) error {
+	request := tag.NewCreateTagRequest()
+	request.TagKey = &key
+	request.TagValue = &value
+
+	_, err := c.tagClient.CreateTag(request)
+	log.Printf("[DEBUG] create tag %s:%s: error: %v", key, value, err)
+	if err != nil {
+		return fmt.Errorf("failed to create tag: %s -> %s: %s", key, value, err)
+	}
+
+	return nil
+}
+
+// DeleteTag create tag by key and value
+func (c *remoteClient) DeleteTag(key, value string) error {
+	request := tag.NewDeleteTagRequest()
+	request.TagKey = &key
+	request.TagValue = &value
+
+	_, err := c.tagClient.DeleteTag(request)
+	log.Printf("[DEBUG] delete tag %s:%s: error: %v", key, value, err)
+	if err != nil {
+		return fmt.Errorf("failed to delete tag: %s -> %s: %s", key, value, err)
+	}
+
+	return nil
+}
diff --git a/go.mod b/go.mod
index e20dab3d76cd069b23898667c2934cd33fca285f..f2368097784a9014c7e92060e8522c3ab45867b0 100644
--- a/go.mod
+++ b/go.mod
@@ -84,6 +84,7 @@ require (
 	github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
 	github.com/keybase/go-crypto v0.0.0-20161004153544-93f5b35093ba // indirect
 	github.com/lib/pq v1.0.0
+	github.com/likexian/gokit v0.20.15
 	github.com/lusis/go-artifactory v0.0.0-20160115162124-7e4ce345df82
 	github.com/masterzen/winrm v0.0.0-20190223112901-5e5c9a7fe54b
 	github.com/mattn/go-colorable v0.1.1
@@ -113,6 +114,8 @@ require (
 	github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a // indirect
 	github.com/soheilhy/cmux v0.1.4 // indirect
 	github.com/spf13/afero v1.2.1
+	github.com/tencentcloud/tencentcloud-sdk-go v3.0.82+incompatible
+	github.com/tencentyun/cos-go-sdk-v5 v0.0.0-20190808065407-f07404cefc8c
 	github.com/terraform-providers/terraform-provider-openstack v1.15.0
 	github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 // indirect
 	github.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5 // indirect
diff --git a/go.sum b/go.sum
index 83f672304d222b8575a10197e967105cee74e9b7..2c5eb21d6bfdfacd0b954f23e43dc03834e7bfe1 100644
--- a/go.sum
+++ b/go.sum
@@ -41,6 +41,7 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022 h1:y8Gs8CzNfDF5AZvjr+5UyGQvQEBL7pwo+v+wX6q9JI8=
 github.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4=
+github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=
 github.com/Unknwon/com v0.0.0-20151008135407-28b053d5a292 h1:tuQ7w+my8a8mkwN7x2TSd7OzTjkZ7rAeSyH4xncuAMI=
 github.com/Unknwon/com v0.0.0-20151008135407-28b053d5a292/go.mod h1:KYCjqMOeHpNuTOiFQU6WEcTG7poCJrUs0YgyHNtn1no=
 github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KMJuWmfCkcxl09JwdlqwDZZ6U14=
@@ -285,6 +286,14 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
 github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
 github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/likexian/gokit v0.0.0-20190309162924-0a377eecf7aa/go.mod h1:QdfYv6y6qPA9pbBA2qXtoT8BMKha6UyNbxWGWl/9Jfk=
+github.com/likexian/gokit v0.0.0-20190418170008-ace88ad0983b/go.mod h1:KKqSnk/VVSW8kEyO2vVCXoanzEutKdlBAPohmGXkxCk=
+github.com/likexian/gokit v0.0.0-20190501133040-e77ea8b19cdc/go.mod h1:3kvONayqCaj+UgrRZGpgfXzHdMYCAO0KAt4/8n0L57Y=
+github.com/likexian/gokit v0.20.15 h1:DgtIqqTRFqtbiLJFzuRESwVrxWxfs8OlY6hnPYBa3BM=
+github.com/likexian/gokit v0.20.15/go.mod h1:kn+nTv3tqh6yhor9BC4Lfiu58SmH8NmQ2PmEl+uM6nU=
+github.com/likexian/simplejson-go v0.0.0-20190409170913-40473a74d76d/go.mod h1:Typ1BfnATYtZ/+/shXfFYLrovhFyuKvzwrdOnIDHlmg=
+github.com/likexian/simplejson-go v0.0.0-20190419151922-c1f9f0b4f084/go.mod h1:U4O1vIJvIKwbMZKUJ62lppfdvkCdVd2nfMimHK81eec=
+github.com/likexian/simplejson-go v0.0.0-20190502021454-d8787b4bfa0b/go.mod h1:3BWwtmKP9cXWwYCr5bkoVDEfLywacOv0s06OBEDpyt8=
 github.com/lusis/go-artifactory v0.0.0-20160115162124-7e4ce345df82 h1:wnfcqULT+N2seWf6y4yHzmi7GD2kNx4Ute0qArktD48=
 github.com/lusis/go-artifactory v0.0.0-20160115162124-7e4ce345df82/go.mod h1:y54tfGmO3NKssKveTEFFzH8C/akrSOy/iW9qEAUDV84=
 github.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9 h1:SmVbOZFWAlyQshuMfOkiAx1f5oUTsOGG5IXplAEYeeM=
@@ -336,6 +345,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/mozillazg/go-httpheader v0.2.1 h1:geV7TrjbL8KXSyvghnFm+NyTux/hxwueTSrwhe88TQQ=
+github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ=
 github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
@@ -392,6 +403,10 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/svanharmelen/jsonapi v0.0.0-20180618144545-0c0828c3f16d h1:Z4EH+5EffvBEhh37F0C0DnpklTMh00JOkjW5zK3ofBI=
 github.com/svanharmelen/jsonapi v0.0.0-20180618144545-0c0828c3f16d/go.mod h1:BSTlc8jOjh0niykqEGVXOLXdi9o0r0kR8tCYiMvjFgw=
+github.com/tencentcloud/tencentcloud-sdk-go v3.0.82+incompatible h1:5Td2b0yfaOvw9M9nZ5Oav6Li9bxUNxt4DgxMfIPpsa0=
+github.com/tencentcloud/tencentcloud-sdk-go v3.0.82+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4=
+github.com/tencentyun/cos-go-sdk-v5 v0.0.0-20190808065407-f07404cefc8c h1:iRD1CqtWUjgEVEmjwTMbP1DMzz1HRytOsgx/rlw/vNs=
+github.com/tencentyun/cos-go-sdk-v5 v0.0.0-20190808065407-f07404cefc8c/go.mod h1:wk2XFUg6egk4tSDNZtXeKfe2G6690UVyt163PuUxBZk=
 github.com/terraform-providers/terraform-provider-openstack v1.15.0 h1:adpjqej+F8BAX9dHmuPF47sUIkgifeqBu6p7iCsyj0Y=
 github.com/terraform-providers/terraform-provider-openstack v1.15.0/go.mod h1:2aQ6n/BtChAl1y2S60vebhyJyZXBsuAI5G4+lHrT1Ew=
 github.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6 h1:lYIiVDtZnyTWlNwiAxLj0bbpTcx1BWCFhXjfsvmPdNc=
diff --git a/vendor/github.com/likexian/gokit/LICENSE b/vendor/github.com/likexian/gokit/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..42d4ca144395bceaabbaf024ca2002cd0a537623
--- /dev/null
+++ b/vendor/github.com/likexian/gokit/LICENSE
@@ -0,0 +1,205 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright 2012-2019 Li Kexian
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+   APPENDIX: Copyright 2012-2019 Li Kexian
+
+       https://www.likexian.com/
diff --git a/vendor/github.com/likexian/gokit/assert/README.md b/vendor/github.com/likexian/gokit/assert/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..8ecb799ce69a64c3b4649500932b48738edd350b
--- /dev/null
+++ b/vendor/github.com/likexian/gokit/assert/README.md
@@ -0,0 +1,98 @@
+# GoKit - assert
+
+Assert kits for Golang development.
+
+## Installation
+
+    go get -u github.com/likexian/gokit
+
+## Importing
+
+    import (
+        "github.com/likexian/gokit/assert"
+    )
+
+## Documentation
+
+Visit the docs on [GoDoc](https://godoc.org/github.com/likexian/gokit/assert)
+
+## Example
+
+### assert panic
+
+```go
+func willItPanic() {
+    panic("failed")
+}
+assert.Panic(t, willItPanic)
+```
+
+### assert err is nil
+
+```go
+fp, err := os.Open("/data/dev/gokit/LICENSE")
+assert.Nil(t, err)
+```
+
+### assert equal
+
+```go
+x := map[string]int{"a": 1, "b": 2}
+y := map[string]int{"a": 1, "b": 2}
+assert.Equal(t, x, y, "x shall equal to y")
+```
+
+### check string in array
+
+```go
+ok := assert.IsContains([]string{"a", "b", "c"}, "b")
+if ok {
+    fmt.Println("value in array")
+} else {
+    fmt.Println("value not in array")
+}
+```
+
+### check string in interface array
+
+```go
+ok := assert.IsContains([]interface{}{0, "1", 2}, "1")
+if ok {
+    fmt.Println("value in array")
+} else {
+    fmt.Println("value not in array")
+}
+```
+
+### check object in struct array
+
+```go
+ok := assert.IsContains([]A{A{0, 1}, A{1, 2}, A{1, 3}}, A{1, 2})
+if ok {
+    fmt.Println("value in array")
+} else {
+    fmt.Println("value not in array")
+}
+```
+
+### a := c ? x : y
+
+```go
+a := 1
+// b := a == 1 ? true : false
+b := assert.If(a == 1, true, false)
+```
+
+## LICENSE
+
+Copyright 2012-2019 Li Kexian
+
+Licensed under the Apache License 2.0
+
+## About
+
+- [Li Kexian](https://www.likexian.com/)
+
+## DONATE
+
+- [Help me make perfect](https://www.likexian.com/donate/)
diff --git a/vendor/github.com/likexian/gokit/assert/assert.go b/vendor/github.com/likexian/gokit/assert/assert.go
new file mode 100644
index 0000000000000000000000000000000000000000..ddc4a6688719ae20c3108d7828883d3946a69317
--- /dev/null
+++ b/vendor/github.com/likexian/gokit/assert/assert.go
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2012-2019 Li Kexian
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * A toolkit for Golang development
+ * https://www.likexian.com/
+ */
+
+package assert
+
+import (
+	"fmt"
+	"reflect"
+	"runtime"
+	"testing"
+)
+
+// Version returns package version
+func Version() string {
+	return "0.10.1"
+}
+
+// Author returns package author
+func Author() string {
+	return "[Li Kexian](https://www.likexian.com/)"
+}
+
+// License returns package license
+func License() string {
+	return "Licensed under the Apache License 2.0"
+}
+
+// Equal assert test value to be equal
+func Equal(t *testing.T, got, exp interface{}, args ...interface{}) {
+	equal(t, got, exp, 1, args...)
+}
+
+// NotEqual assert test value to be not equal
+func NotEqual(t *testing.T, got, exp interface{}, args ...interface{}) {
+	notEqual(t, got, exp, 1, args...)
+}
+
+// Nil assert test value to be nil
+func Nil(t *testing.T, got interface{}, args ...interface{}) {
+	equal(t, got, nil, 1, args...)
+}
+
+// NotNil assert test value to be not nil
+func NotNil(t *testing.T, got interface{}, args ...interface{}) {
+	notEqual(t, got, nil, 1, args...)
+}
+
+// True assert test value to be true
+func True(t *testing.T, got interface{}, args ...interface{}) {
+	equal(t, got, true, 1, args...)
+}
+
+// False assert test value to be false
+func False(t *testing.T, got interface{}, args ...interface{}) {
+	notEqual(t, got, true, 1, args...)
+}
+
+// Zero assert test value to be zero value
+func Zero(t *testing.T, got interface{}, args ...interface{}) {
+	equal(t, IsZero(got), true, 1, args...)
+}
+
+// NotZero assert test value to be not zero value
+func NotZero(t *testing.T, got interface{}, args ...interface{}) {
+	notEqual(t, IsZero(got), true, 1, args...)
+}
+
+// Len assert length of test vaue to be exp
+func Len(t *testing.T, got interface{}, exp int, args ...interface{}) {
+	equal(t, Length(got), exp, 1, args...)
+}
+
+// NotLen assert length of test vaue to be not exp
+func NotLen(t *testing.T, got interface{}, exp int, args ...interface{}) {
+	notEqual(t, Length(got), exp, 1, args...)
+}
+
+// Contains assert test value to be contains
+func Contains(t *testing.T, got, exp interface{}, args ...interface{}) {
+	equal(t, IsContains(got, exp), true, 1, args...)
+}
+
+// NotContains assert test value to be contains
+func NotContains(t *testing.T, got, exp interface{}, args ...interface{}) {
+	notEqual(t, IsContains(got, exp), true, 1, args...)
+}
+
+// Match assert test value match exp pattern
+func Match(t *testing.T, got, exp interface{}, args ...interface{}) {
+	equal(t, IsMatch(got, exp), true, 1, args...)
+}
+
+// NotMatch assert test value not match exp pattern
+func NotMatch(t *testing.T, got, exp interface{}, args ...interface{}) {
+	notEqual(t, IsMatch(got, exp), true, 1, args...)
+}
+
+// Lt assert test value less than exp
+func Lt(t *testing.T, got, exp interface{}, args ...interface{}) {
+	equal(t, IsLt(got, exp), true, 1, args...)
+}
+
+// Le assert test value less than exp or equal
+func Le(t *testing.T, got, exp interface{}, args ...interface{}) {
+	equal(t, IsLe(got, exp), true, 1, args...)
+}
+
+// Gt assert test value greater than exp
+func Gt(t *testing.T, got, exp interface{}, args ...interface{}) {
+	equal(t, IsGt(got, exp), true, 1, args...)
+}
+
+// Ge assert test value greater than exp or equal
+func Ge(t *testing.T, got, exp interface{}, args ...interface{}) {
+	equal(t, IsGe(got, exp), true, 1, args...)
+}
+
+// Panic assert testing to be panic
+func Panic(t *testing.T, fn func(), args ...interface{}) {
+	defer func() {
+		ff := func() {
+			t.Error("! -", "assert expected to be panic")
+			if len(args) > 0 {
+				t.Error("! -", fmt.Sprint(args...))
+			}
+		}
+		ok := recover() != nil
+		assert(t, ok, ff, 2)
+	}()
+
+	fn()
+}
+
+// NotPanic assert testing to be panic
+func NotPanic(t *testing.T, fn func(), args ...interface{}) {
+	defer func() {
+		ff := func() {
+			t.Error("! -", "assert expected to be not panic")
+			if len(args) > 0 {
+				t.Error("! -", fmt.Sprint(args...))
+			}
+		}
+		ok := recover() == nil
+		assert(t, ok, ff, 3)
+	}()
+
+	fn()
+}
+
+func equal(t *testing.T, got, exp interface{}, step int, args ...interface{}) {
+	fn := func() {
+		switch got.(type) {
+		case error:
+			t.Errorf("! unexpected error: \"%s\"", got)
+		default:
+			t.Errorf("! expected %#v, but got %#v", exp, got)
+		}
+		if len(args) > 0 {
+			t.Error("! -", fmt.Sprint(args...))
+		}
+	}
+	ok := reflect.DeepEqual(exp, got)
+	assert(t, ok, fn, step+1)
+}
+
+func notEqual(t *testing.T, got, exp interface{}, step int, args ...interface{}) {
+	fn := func() {
+		t.Errorf("! unexpected: %#v", got)
+		if len(args) > 0 {
+			t.Error("! -", fmt.Sprint(args...))
+		}
+	}
+	ok := !reflect.DeepEqual(exp, got)
+	assert(t, ok, fn, step+1)
+}
+
+func assert(t *testing.T, pass bool, fn func(), step int) {
+	if !pass {
+		_, file, line, ok := runtime.Caller(step + 1)
+		if ok {
+			t.Errorf("%s:%d", file, line)
+		}
+		fn()
+		t.FailNow()
+	}
+}
diff --git a/vendor/github.com/likexian/gokit/assert/values.go b/vendor/github.com/likexian/gokit/assert/values.go
new file mode 100644
index 0000000000000000000000000000000000000000..02c303ab17ff9577c12ec008f0d49e97364f00f2
--- /dev/null
+++ b/vendor/github.com/likexian/gokit/assert/values.go
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2012-2019 Li Kexian
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * A toolkit for Golang development
+ * https://www.likexian.com/
+ */
+
+package assert
+
+import (
+	"errors"
+	"fmt"
+	"reflect"
+	"regexp"
+	"strconv"
+	"strings"
+)
+
+// ErrInvalid is value invalid for operation
+var ErrInvalid = errors.New("value if invalid")
+
+// ErrLess is expect to be greater error
+var ErrLess = errors.New("left is less the right")
+
+// ErrGreater is expect to be less error
+var ErrGreater = errors.New("left is greater then right")
+
+// CMP is compare operation
+var CMP = struct {
+	LT string
+	LE string
+	GT string
+	GE string
+}{
+	"<",
+	"<=",
+	">",
+	">=",
+}
+
+// IsZero returns value is zero value
+func IsZero(v interface{}) bool {
+	vv := reflect.ValueOf(v)
+	switch vv.Kind() {
+	case reflect.Invalid:
+		return true
+	case reflect.Bool:
+		return !vv.Bool()
+	case reflect.Ptr, reflect.Interface:
+		return vv.IsNil()
+	case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
+		return vv.Len() == 0
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		return vv.Int() == 0
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+		return vv.Uint() == 0
+	case reflect.Float32, reflect.Float64:
+		return vv.Float() == 0
+	default:
+		return false
+	}
+}
+
+// IsContains returns whether value is within array
+func IsContains(array interface{}, value interface{}) bool {
+	vv := reflect.ValueOf(array)
+	if vv.Kind() == reflect.Ptr || vv.Kind() == reflect.Interface {
+		if vv.IsNil() {
+			return false
+		}
+		vv = vv.Elem()
+	}
+
+	switch vv.Kind() {
+	case reflect.Invalid:
+		return false
+	case reflect.Slice:
+		for i := 0; i < vv.Len(); i++ {
+			if reflect.DeepEqual(value, vv.Index(i).Interface()) {
+				return true
+			}
+		}
+		return false
+	case reflect.Map:
+		s := vv.MapKeys()
+		for i := 0; i < len(s); i++ {
+			if reflect.DeepEqual(value, s[i].Interface()) {
+				return true
+			}
+		}
+		return false
+	case reflect.String:
+		ss := reflect.ValueOf(value)
+		switch ss.Kind() {
+		case reflect.String:
+			return strings.Contains(vv.String(), ss.String())
+		}
+		return false
+	default:
+		return reflect.DeepEqual(array, value)
+	}
+}
+
+// IsMatch returns if value v contains any match of pattern r
+//   IsMatch(regexp.MustCompile("v\d+"), "v100")
+//   IsMatch("v\d+", "v100")
+//   IsMatch("\d+\.\d+", 100.1)
+func IsMatch(r interface{}, v interface{}) bool {
+	var re *regexp.Regexp
+
+	if v, ok := r.(*regexp.Regexp); ok {
+		re = v
+	} else {
+		re = regexp.MustCompile(fmt.Sprint(r))
+	}
+
+	return re.MatchString(fmt.Sprint(v))
+}
+
+// Length returns length of value
+func Length(v interface{}) int {
+	vv := reflect.ValueOf(v)
+	if vv.Kind() == reflect.Ptr || vv.Kind() == reflect.Interface {
+		if vv.IsNil() {
+			return 0
+		}
+		vv = vv.Elem()
+	}
+
+	switch vv.Kind() {
+	case reflect.Invalid:
+		return 0
+	case reflect.Ptr, reflect.Interface:
+		return 0
+	case reflect.Array, reflect.Slice, reflect.Map, reflect.String:
+		return vv.Len()
+	default:
+		return len(fmt.Sprintf("%#v", v))
+	}
+}
+
+// IsLt returns if x less than y, value invalid will returns false
+func IsLt(x, y interface{}) bool {
+	return Compare(x, y, CMP.LT) == nil
+}
+
+// IsLe returns if x less than or equal to y, value invalid will returns false
+func IsLe(x, y interface{}) bool {
+	return Compare(x, y, CMP.LE) == nil
+}
+
+// IsGt returns if x greater than y, value invalid will returns false
+func IsGt(x, y interface{}) bool {
+	return Compare(x, y, CMP.GT) == nil
+}
+
+// IsGe returns if x greater than or equal to y, value invalid will returns false
+func IsGe(x, y interface{}) bool {
+	return Compare(x, y, CMP.GE) == nil
+}
+
+// Compare compare x and y, by operation
+// It returns nil for true, ErrInvalid for invalid operation, err for false
+//   Compare(1, 2, ">") // number compare -> true
+//   Compare("a", "a", ">=") // string compare -> true
+//   Compare([]string{"a", "b"}, []string{"a"}, "<") // slice len compare -> false
+func Compare(x, y interface{}, op string) error {
+	if !IsContains([]string{CMP.LT, CMP.LE, CMP.GT, CMP.GE}, op) {
+		return ErrInvalid
+	}
+
+	vv := reflect.ValueOf(x)
+	if vv.Kind() == reflect.Ptr || vv.Kind() == reflect.Interface {
+		if vv.IsNil() {
+			return ErrInvalid
+		}
+		vv = vv.Elem()
+	}
+
+	var c float64
+	switch vv.Kind() {
+	case reflect.Invalid:
+		return ErrInvalid
+	case reflect.String:
+		yy := reflect.ValueOf(y)
+		switch yy.Kind() {
+		case reflect.String:
+			c = float64(strings.Compare(vv.String(), yy.String()))
+		default:
+			return ErrInvalid
+		}
+	case reflect.Slice, reflect.Map, reflect.Array:
+		yy := reflect.ValueOf(y)
+		switch yy.Kind() {
+		case reflect.Slice, reflect.Map, reflect.Array:
+			c = float64(vv.Len() - yy.Len())
+		default:
+			return ErrInvalid
+		}
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		yy, err := ToInt64(y)
+		if err != nil {
+			return ErrInvalid
+		}
+		c = float64(vv.Int() - yy)
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+		yy, err := ToUint64(y)
+		if err != nil {
+			return ErrInvalid
+		}
+		c = float64(vv.Uint()) - float64(yy)
+	case reflect.Float32, reflect.Float64:
+		yy, err := ToFloat64(y)
+		if err != nil {
+			return ErrInvalid
+		}
+		c = float64(vv.Float() - yy)
+	default:
+		return ErrInvalid
+	}
+
+	switch {
+	case c < 0:
+		switch op {
+		case CMP.LT, CMP.LE:
+			return nil
+		default:
+			return ErrLess
+		}
+	case c > 0:
+		switch op {
+		case CMP.GT, CMP.GE:
+			return nil
+		default:
+			return ErrGreater
+		}
+	default:
+		switch op {
+		case CMP.LT:
+			return ErrGreater
+		case CMP.GT:
+			return ErrLess
+		default:
+			return nil
+		}
+	}
+}
+
+// ToInt64 returns int value for int or uint or float
+func ToInt64(v interface{}) (int64, error) {
+	vv := reflect.ValueOf(v)
+	switch vv.Kind() {
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		return int64(vv.Int()), nil
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+		return int64(vv.Uint()), nil
+	case reflect.Float32, reflect.Float64:
+		return int64(vv.Float()), nil
+	case reflect.String:
+		r, err := strconv.ParseInt(vv.String(), 10, 64)
+		if err != nil {
+			return 0, ErrInvalid
+		}
+		return r, nil
+	default:
+		return 0, ErrInvalid
+	}
+}
+
+// ToUint64 returns uint value for int or uint or float
+func ToUint64(v interface{}) (uint64, error) {
+	vv := reflect.ValueOf(v)
+	switch vv.Kind() {
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		return uint64(vv.Int()), nil
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+		return uint64(vv.Uint()), nil
+	case reflect.Float32, reflect.Float64:
+		return uint64(vv.Float()), nil
+	case reflect.String:
+		r, err := strconv.ParseUint(vv.String(), 10, 64)
+		if err != nil {
+			return 0, ErrInvalid
+		}
+		return r, nil
+	default:
+		return 0, ErrInvalid
+	}
+}
+
+// ToFloat64 returns float64 value for int or uint or float
+func ToFloat64(v interface{}) (float64, error) {
+	vv := reflect.ValueOf(v)
+	switch vv.Kind() {
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		return float64(vv.Int()), nil
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+		return float64(vv.Uint()), nil
+	case reflect.Float32, reflect.Float64:
+		return float64(vv.Float()), nil
+	case reflect.String:
+		r, err := strconv.ParseFloat(vv.String(), 64)
+		if err != nil {
+			return 0, ErrInvalid
+		}
+		return r, nil
+	default:
+		return 0, ErrInvalid
+	}
+}
+
+// If returns x if c is true, else y
+//   z = If(c, x, y)
+// equal to:
+//   z = c ? x : y
+func If(c bool, x, y interface{}) interface{} {
+	if c {
+		return x
+	}
+
+	return y
+}
diff --git a/vendor/github.com/mozillazg/go-httpheader/.bumpversion.cfg b/vendor/github.com/mozillazg/go-httpheader/.bumpversion.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..18dad225a6af2af44e3fef50064e6376001a762f
--- /dev/null
+++ b/vendor/github.com/mozillazg/go-httpheader/.bumpversion.cfg
@@ -0,0 +1,7 @@
+[bumpversion]
+commit = True
+tag = True
+current_version = 0.2.1
+
+[bumpversion:file:encode.go]
+
diff --git a/vendor/github.com/mozillazg/go-httpheader/.gitignore b/vendor/github.com/mozillazg/go-httpheader/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..098e254bc85395e8fbf03301abc5dcf818b89c0f
--- /dev/null
+++ b/vendor/github.com/mozillazg/go-httpheader/.gitignore
@@ -0,0 +1,27 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
+dist/
+cover.html
+cover.out
diff --git a/vendor/github.com/mozillazg/go-httpheader/.travis.yml b/vendor/github.com/mozillazg/go-httpheader/.travis.yml
new file mode 100644
index 0000000000000000000000000000000000000000..fbb97928b58109b3692c896d0eadd9c6ca93fc8f
--- /dev/null
+++ b/vendor/github.com/mozillazg/go-httpheader/.travis.yml
@@ -0,0 +1,25 @@
+language: go
+go:
+  - 1.6
+  - 1.7
+  - 1.8
+  - tip
+
+sudo: false
+
+before_install:
+  - go get github.com/mattn/goveralls
+
+install:
+  - go get
+  - go build
+
+script:
+  - make test
+  - $HOME/gopath/bin/goveralls -service=travis-ci -ignore=vendor/
+
+matrix:
+  allow_failures:
+    - go: 1.6
+    - go: 1.7
+    - go: tip
diff --git a/vendor/github.com/mozillazg/go-httpheader/CHANGELOG.md b/vendor/github.com/mozillazg/go-httpheader/CHANGELOG.md
new file mode 100644
index 0000000000000000000000000000000000000000..7b643fd64d197cc2da488479550b0ed69cc17264
--- /dev/null
+++ b/vendor/github.com/mozillazg/go-httpheader/CHANGELOG.md
@@ -0,0 +1,15 @@
+# Changelog
+
+## 0.2.1 (2018-11-03)
+
+* add go.mod file to identify as a module
+
+
+## 0.2.0 (2017-06-24)
+
+* support http.Header field.
+
+
+## 0.1.0 (2017-06-10)
+
+* Initial Release
diff --git a/vendor/github.com/mozillazg/go-httpheader/LICENSE b/vendor/github.com/mozillazg/go-httpheader/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..8ff7942e2030d9fa4bb1c8766d184abd9d45a695
--- /dev/null
+++ b/vendor/github.com/mozillazg/go-httpheader/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 mozillazg
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/mozillazg/go-httpheader/Makefile b/vendor/github.com/mozillazg/go-httpheader/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..aadfe8298e16f12cdb060e6e6cb559a37ab035b9
--- /dev/null
+++ b/vendor/github.com/mozillazg/go-httpheader/Makefile
@@ -0,0 +1,15 @@
+help:
+	@echo "test             run test"
+	@echo "lint             run lint"
+
+.PHONY: test
+test:
+	go test -v -cover -coverprofile cover.out
+	go tool cover -html=cover.out -o cover.html
+
+.PHONY: lint
+lint:
+	gofmt -s -w .
+	goimports -w .
+	golint .
+	go vet
diff --git a/vendor/github.com/mozillazg/go-httpheader/README.md b/vendor/github.com/mozillazg/go-httpheader/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..d73daf64ce77f2fc0386e58ceee7056d8574ab60
--- /dev/null
+++ b/vendor/github.com/mozillazg/go-httpheader/README.md
@@ -0,0 +1,63 @@
+# go-httpheader
+
+go-httpheader is a Go library for encoding structs into Header fields.
+
+[![Build Status](https://img.shields.io/travis/mozillazg/go-httpheader/master.svg)](https://travis-ci.org/mozillazg/go-httpheader)
+[![Coverage Status](https://img.shields.io/coveralls/mozillazg/go-httpheader/master.svg)](https://coveralls.io/r/mozillazg/go-httpheader?branch=master)
+[![Go Report Card](https://goreportcard.com/badge/github.com/mozillazg/go-httpheader)](https://goreportcard.com/report/github.com/mozillazg/go-httpheader)
+[![GoDoc](https://godoc.org/github.com/mozillazg/go-httpheader?status.svg)](https://godoc.org/github.com/mozillazg/go-httpheader)
+
+## install
+
+`go get -u github.com/mozillazg/go-httpheader`
+
+
+## usage
+
+```go
+package main
+
+import (
+	"fmt"
+	"net/http"
+
+	"github.com/mozillazg/go-httpheader"
+)
+
+type Options struct {
+	hide         string
+	ContentType  string `header:"Content-Type"`
+	Length       int
+	XArray       []string `header:"X-Array"`
+	TestHide     string   `header:"-"`
+	IgnoreEmpty  string   `header:"X-Empty,omitempty"`
+	IgnoreEmptyN string   `header:"X-Empty-N,omitempty"`
+	CustomHeader http.Header
+}
+
+func main() {
+	opt := Options{
+		hide:         "hide",
+		ContentType:  "application/json",
+		Length:       2,
+		XArray:       []string{"test1", "test2"},
+		TestHide:     "hide",
+		IgnoreEmptyN: "n",
+		CustomHeader: http.Header{
+			"X-Test-1": []string{"233"},
+			"X-Test-2": []string{"666"},
+		},
+	}
+	h, _ := httpheader.Header(opt)
+	fmt.Printf("%#v", h)
+	// h:
+	// http.Header{
+	//	"X-Test-1":     []string{"233"},
+	//	"X-Test-2":     []string{"666"},
+	//	"Content-Type": []string{"application/json"},
+	//	"Length":       []string{"2"},
+	//	"X-Array":      []string{"test1", "test2"},
+	//	"X-Empty-N":    []string{"n"},
+	//}
+}
+```
diff --git a/vendor/github.com/mozillazg/go-httpheader/encode.go b/vendor/github.com/mozillazg/go-httpheader/encode.go
new file mode 100644
index 0000000000000000000000000000000000000000..c122f2b8823ae5be2126bbf763a9b1a82fae19e0
--- /dev/null
+++ b/vendor/github.com/mozillazg/go-httpheader/encode.go
@@ -0,0 +1,290 @@
+// Package query implements encoding of structs into http.Header fields.
+//
+// As a simple example:
+//
+// 	type Options struct {
+// 		ContentType  string `header:"Content-Type"`
+// 		Length       int
+// 	}
+//
+// 	opt := Options{"application/json", 2}
+// 	h, _ := httpheader.Header(opt)
+// 	fmt.Printf("%#v", h)
+// 	// will output:
+// 	// http.Header{"Content-Type":[]string{"application/json"},"Length":[]string{"2"}}
+//
+// The exact mapping between Go values and http.Header is described in the
+// documentation for the Header() function.
+package httpheader
+
+import (
+	"fmt"
+	"net/http"
+	"reflect"
+	"strconv"
+	"strings"
+	"time"
+)
+
+const tagName = "header"
+
+// Version ...
+const Version = "0.2.1"
+
+var timeType = reflect.TypeOf(time.Time{})
+var headerType = reflect.TypeOf(http.Header{})
+
+var encoderType = reflect.TypeOf(new(Encoder)).Elem()
+
+// Encoder is an interface implemented by any type that wishes to encode
+// itself into Header fields in a non-standard way.
+type Encoder interface {
+	EncodeHeader(key string, v *http.Header) error
+}
+
+// Header returns the http.Header encoding of v.
+//
+// Header expects to be passed a struct, and traverses it recursively using the
+// following encoding rules.
+//
+// Each exported struct field is encoded as a Header field unless
+//
+//	- the field's tag is "-", or
+//	- the field is empty and its tag specifies the "omitempty" option
+//
+// The empty values are false, 0, any nil pointer or interface value, any array
+// slice, map, or string of length zero, and any time.Time that returns true
+// for IsZero().
+//
+// The Header field name defaults to the struct field name but can be
+// specified in the struct field's tag value.  The "header" key in the struct
+// field's tag value is the key name, followed by an optional comma and
+// options.  For example:
+//
+// 	// Field is ignored by this package.
+// 	Field int `header:"-"`
+//
+// 	// Field appears as Header field "X-Name".
+// 	Field int `header:"X-Name"`
+//
+// 	// Field appears as Header field "X-Name" and the field is omitted if
+// 	// its value is empty
+// 	Field int `header:"X-Name,omitempty"`
+//
+// 	// Field appears as Header field "Field" (the default), but the field
+// 	// is skipped if empty.  Note the leading comma.
+// 	Field int `header:",omitempty"`
+//
+// For encoding individual field values, the following type-dependent rules
+// apply:
+//
+// Boolean values default to encoding as the strings "true" or "false".
+// Including the "int" option signals that the field should be encoded as the
+// strings "1" or "0".
+//
+// time.Time values default to encoding as RFC1123("Mon, 02 Jan 2006 15:04:05 GMT")
+// timestamps. Including the "unix" option signals that the field should be
+// encoded as a Unix time (see time.Unix())
+//
+// Slice and Array values default to encoding as multiple Header values of the
+// same name. example:
+// X-Name: []string{"Tom", "Jim"}, etc.
+//
+// http.Header values will be used to extend the Header fields.
+//
+// Anonymous struct fields are usually encoded as if their inner exported
+// fields were fields in the outer struct, subject to the standard Go
+// visibility rules. An anonymous struct field with a name given in its Header
+// tag is treated as having that name, rather than being anonymous.
+//
+// Non-nil pointer values are encoded as the value pointed to.
+//
+// All other values are encoded using their default string representation.
+//
+// Multiple fields that encode to the same Header filed name will be included
+// as multiple Header values of the same name.
+func Header(v interface{}) (http.Header, error) {
+	h := make(http.Header)
+	val := reflect.ValueOf(v)
+	for val.Kind() == reflect.Ptr {
+		if val.IsNil() {
+			return h, nil
+		}
+		val = val.Elem()
+	}
+
+	if v == nil {
+		return h, nil
+	}
+
+	if val.Kind() != reflect.Struct {
+		return nil, fmt.Errorf("httpheader: Header() expects struct input. Got %v", val.Kind())
+	}
+
+	err := reflectValue(h, val)
+	return h, err
+}
+
+// reflectValue populates the header fields from the struct fields in val.
+// Embedded structs are followed recursively (using the rules defined in the
+// Values function documentation) breadth-first.
+func reflectValue(header http.Header, val reflect.Value) error {
+	var embedded []reflect.Value
+
+	typ := val.Type()
+	for i := 0; i < typ.NumField(); i++ {
+		sf := typ.Field(i)
+		if sf.PkgPath != "" && !sf.Anonymous { // unexported
+			continue
+		}
+
+		sv := val.Field(i)
+		tag := sf.Tag.Get(tagName)
+		if tag == "-" {
+			continue
+		}
+		name, opts := parseTag(tag)
+		if name == "" {
+			if sf.Anonymous && sv.Kind() == reflect.Struct {
+				// save embedded struct for later processing
+				embedded = append(embedded, sv)
+				continue
+			}
+
+			name = sf.Name
+		}
+
+		if opts.Contains("omitempty") && isEmptyValue(sv) {
+			continue
+		}
+
+		if sv.Type().Implements(encoderType) {
+			if !reflect.Indirect(sv).IsValid() {
+				sv = reflect.New(sv.Type().Elem())
+			}
+
+			m := sv.Interface().(Encoder)
+			if err := m.EncodeHeader(name, &header); err != nil {
+				return err
+			}
+			continue
+		}
+
+		if sv.Kind() == reflect.Slice || sv.Kind() == reflect.Array {
+			for i := 0; i < sv.Len(); i++ {
+				k := name
+				header.Add(k, valueString(sv.Index(i), opts))
+			}
+			continue
+		}
+
+		for sv.Kind() == reflect.Ptr {
+			if sv.IsNil() {
+				break
+			}
+			sv = sv.Elem()
+		}
+
+		if sv.Type() == timeType {
+			header.Add(name, valueString(sv, opts))
+			continue
+		}
+		if sv.Type() == headerType {
+			h := sv.Interface().(http.Header)
+			for k, vs := range h {
+				for _, v := range vs {
+					header.Add(k, v)
+				}
+			}
+			continue
+		}
+
+		if sv.Kind() == reflect.Struct {
+			reflectValue(header, sv)
+			continue
+		}
+
+		header.Add(name, valueString(sv, opts))
+	}
+
+	for _, f := range embedded {
+		if err := reflectValue(header, f); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// valueString returns the string representation of a value.
+func valueString(v reflect.Value, opts tagOptions) string {
+	for v.Kind() == reflect.Ptr {
+		if v.IsNil() {
+			return ""
+		}
+		v = v.Elem()
+	}
+
+	if v.Kind() == reflect.Bool && opts.Contains("int") {
+		if v.Bool() {
+			return "1"
+		}
+		return "0"
+	}
+
+	if v.Type() == timeType {
+		t := v.Interface().(time.Time)
+		if opts.Contains("unix") {
+			return strconv.FormatInt(t.Unix(), 10)
+		}
+		return t.Format(http.TimeFormat)
+	}
+
+	return fmt.Sprint(v.Interface())
+}
+
+// isEmptyValue checks if a value should be considered empty for the purposes
+// of omitting fields with the "omitempty" option.
+func isEmptyValue(v reflect.Value) bool {
+	switch v.Kind() {
+	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
+		return v.Len() == 0
+	case reflect.Bool:
+		return !v.Bool()
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		return v.Int() == 0
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+		return v.Uint() == 0
+	case reflect.Float32, reflect.Float64:
+		return v.Float() == 0
+	case reflect.Interface, reflect.Ptr:
+		return v.IsNil()
+	}
+
+	if v.Type() == timeType {
+		return v.Interface().(time.Time).IsZero()
+	}
+
+	return false
+}
+
+// tagOptions is the string following a comma in a struct field's "header" tag, or
+// the empty string. It does not include the leading comma.
+type tagOptions []string
+
+// parseTag splits a struct field's header tag into its name and comma-separated
+// options.
+func parseTag(tag string) (string, tagOptions) {
+	s := strings.Split(tag, ",")
+	return s[0], s[1:]
+}
+
+// Contains checks whether the tagOptions contains the specified option.
+func (o tagOptions) Contains(option string) bool {
+	for _, s := range o {
+		if s == option {
+			return true
+		}
+	}
+	return false
+}
diff --git a/vendor/github.com/mozillazg/go-httpheader/go.mod b/vendor/github.com/mozillazg/go-httpheader/go.mod
new file mode 100644
index 0000000000000000000000000000000000000000..27af234ebffd346c1772492a62943accf289733e
--- /dev/null
+++ b/vendor/github.com/mozillazg/go-httpheader/go.mod
@@ -0,0 +1 @@
+module github.com/mozillazg/go-httpheader
diff --git a/vendor/github.com/tencentcloud/tencentcloud-sdk-go/LICENSE b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..efc75a2253eac0053be445ee5f529090466b9faf
--- /dev/null
+++ b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/LICENSE
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright (c) 2017-2018 Tencent Ltd.
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/client.go b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..1927cb568ca0204b51531a6626b2ba0d85ca44c0
--- /dev/null
+++ b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/client.go
@@ -0,0 +1,261 @@
+package common
+
+import (
+	"encoding/hex"
+	"encoding/json"
+	"fmt"
+	"log"
+	"net/http"
+	"net/http/httputil"
+	"strconv"
+	"strings"
+	"time"
+
+	tchttp "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http"
+	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
+)
+
+type Client struct {
+	region          string
+	httpClient      *http.Client
+	httpProfile     *profile.HttpProfile
+	profile         *profile.ClientProfile
+	credential      *Credential
+	signMethod      string
+	unsignedPayload bool
+	debug           bool
+}
+
+func (c *Client) Send(request tchttp.Request, response tchttp.Response) (err error) {
+	if request.GetDomain() == "" {
+		domain := c.httpProfile.Endpoint
+		if domain == "" {
+			domain = tchttp.GetServiceDomain(request.GetService())
+		}
+		request.SetDomain(domain)
+	}
+
+	if request.GetHttpMethod() == "" {
+		request.SetHttpMethod(c.httpProfile.ReqMethod)
+	}
+
+	tchttp.CompleteCommonParams(request, c.GetRegion())
+
+	if c.signMethod == "HmacSHA1" || c.signMethod == "HmacSHA256" {
+		return c.sendWithSignatureV1(request, response)
+	} else {
+		return c.sendWithSignatureV3(request, response)
+	}
+}
+
+func (c *Client) sendWithSignatureV1(request tchttp.Request, response tchttp.Response) (err error) {
+	// TODO: not an elegant way, it should be done in common params, but finally it need to refactor
+	request.GetParams()["Language"] = c.profile.Language
+	err = tchttp.ConstructParams(request)
+	if err != nil {
+		return err
+	}
+	err = signRequest(request, c.credential, c.signMethod)
+	if err != nil {
+		return err
+	}
+	httpRequest, err := http.NewRequest(request.GetHttpMethod(), request.GetUrl(), request.GetBodyReader())
+	if err != nil {
+		return err
+	}
+	if request.GetHttpMethod() == "POST" {
+		httpRequest.Header["Content-Type"] = []string{"application/x-www-form-urlencoded"}
+	}
+	if c.debug {
+		outbytes, err := httputil.DumpRequest(httpRequest, true)
+		if err != nil {
+			log.Printf("[ERROR] dump request failed because %s", err)
+			return err
+		}
+		log.Printf("[DEBUG] http request = %s", outbytes)
+	}
+	httpResponse, err := c.httpClient.Do(httpRequest)
+	if err != nil {
+		return err
+	}
+	err = tchttp.ParseFromHttpResponse(httpResponse, response)
+	return err
+}
+
+func (c *Client) sendWithSignatureV3(request tchttp.Request, response tchttp.Response) (err error) {
+	headers := map[string]string{
+		"Host":               request.GetDomain(),
+		"X-TC-Action":        request.GetAction(),
+		"X-TC-Version":       request.GetVersion(),
+		"X-TC-Timestamp":     request.GetParams()["Timestamp"],
+		"X-TC-RequestClient": request.GetParams()["RequestClient"],
+		"X-TC-Language":      c.profile.Language,
+	}
+	if c.region != "" {
+		headers["X-TC-Region"] = c.region
+	}
+	if c.credential.Token != "" {
+		headers["X-TC-Token"] = c.credential.Token
+	}
+	if request.GetHttpMethod() == "GET" {
+		headers["Content-Type"] = "application/x-www-form-urlencoded"
+	} else {
+		headers["Content-Type"] = "application/json"
+	}
+
+	// start signature v3 process
+
+	// build canonical request string
+	httpRequestMethod := request.GetHttpMethod()
+	canonicalURI := "/"
+	canonicalQueryString := ""
+	if httpRequestMethod == "GET" {
+		err = tchttp.ConstructParams(request)
+		if err != nil {
+			return err
+		}
+		params := make(map[string]string)
+		for key, value := range request.GetParams() {
+			params[key] = value
+		}
+		delete(params, "Action")
+		delete(params, "Version")
+		delete(params, "Nonce")
+		delete(params, "Region")
+		delete(params, "RequestClient")
+		delete(params, "Timestamp")
+		canonicalQueryString = tchttp.GetUrlQueriesEncoded(params)
+	}
+	canonicalHeaders := fmt.Sprintf("content-type:%s\nhost:%s\n", headers["Content-Type"], headers["Host"])
+	signedHeaders := "content-type;host"
+	requestPayload := ""
+	if httpRequestMethod == "POST" {
+		b, err := json.Marshal(request)
+		if err != nil {
+			return err
+		}
+		requestPayload = string(b)
+	}
+	hashedRequestPayload := ""
+	if c.unsignedPayload {
+		hashedRequestPayload = sha256hex("UNSIGNED-PAYLOAD")
+		headers["X-TC-Content-SHA256"] = "UNSIGNED-PAYLOAD"
+	} else {
+		hashedRequestPayload = sha256hex(requestPayload)
+	}
+	canonicalRequest := fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s",
+		httpRequestMethod,
+		canonicalURI,
+		canonicalQueryString,
+		canonicalHeaders,
+		signedHeaders,
+		hashedRequestPayload)
+	//log.Println("canonicalRequest:", canonicalRequest)
+
+	// build string to sign
+	algorithm := "TC3-HMAC-SHA256"
+	requestTimestamp := headers["X-TC-Timestamp"]
+	timestamp, _ := strconv.ParseInt(requestTimestamp, 10, 64)
+	t := time.Unix(timestamp, 0).UTC()
+	// must be the format 2006-01-02, ref to package time for more info
+	date := t.Format("2006-01-02")
+	credentialScope := fmt.Sprintf("%s/%s/tc3_request", date, request.GetService())
+	hashedCanonicalRequest := sha256hex(canonicalRequest)
+	string2sign := fmt.Sprintf("%s\n%s\n%s\n%s",
+		algorithm,
+		requestTimestamp,
+		credentialScope,
+		hashedCanonicalRequest)
+	//log.Println("string2sign", string2sign)
+
+	// sign string
+	secretDate := hmacsha256(date, "TC3"+c.credential.SecretKey)
+	secretService := hmacsha256(request.GetService(), secretDate)
+	secretKey := hmacsha256("tc3_request", secretService)
+	signature := hex.EncodeToString([]byte(hmacsha256(string2sign, secretKey)))
+	//log.Println("signature", signature)
+
+	// build authorization
+	authorization := fmt.Sprintf("%s Credential=%s/%s, SignedHeaders=%s, Signature=%s",
+		algorithm,
+		c.credential.SecretId,
+		credentialScope,
+		signedHeaders,
+		signature)
+	//log.Println("authorization", authorization)
+
+	headers["Authorization"] = authorization
+	url := "https://" + request.GetDomain() + request.GetPath()
+	if canonicalQueryString != "" {
+		url = url + "?" + canonicalQueryString
+	}
+	httpRequest, err := http.NewRequest(httpRequestMethod, url, strings.NewReader(requestPayload))
+	if err != nil {
+		return err
+	}
+	for k, v := range headers {
+		httpRequest.Header[k] = []string{v}
+	}
+	if c.debug {
+		outbytes, err := httputil.DumpRequest(httpRequest, true)
+		if err != nil {
+			log.Printf("[ERROR] dump request failed because %s", err)
+			return err
+		}
+		log.Printf("[DEBUG] http request = %s", outbytes)
+	}
+	httpResponse, err := c.httpClient.Do(httpRequest)
+	if err != nil {
+		return err
+	}
+	err = tchttp.ParseFromHttpResponse(httpResponse, response)
+	return err
+}
+
+func (c *Client) GetRegion() string {
+	return c.region
+}
+
+func (c *Client) Init(region string) *Client {
+	c.httpClient = &http.Client{}
+	c.region = region
+	c.signMethod = "TC3-HMAC-SHA256"
+	c.debug = false
+	log.SetFlags(log.LstdFlags | log.Lshortfile)
+	return c
+}
+
+func (c *Client) WithSecretId(secretId, secretKey string) *Client {
+	c.credential = NewCredential(secretId, secretKey)
+	return c
+}
+
+func (c *Client) WithCredential(cred *Credential) *Client {
+	c.credential = cred
+	return c
+}
+
+func (c *Client) WithProfile(clientProfile *profile.ClientProfile) *Client {
+	c.profile = clientProfile
+	c.signMethod = clientProfile.SignMethod
+	c.unsignedPayload = clientProfile.UnsignedPayload
+	c.httpProfile = clientProfile.HttpProfile
+	c.httpClient.Timeout = time.Duration(c.httpProfile.ReqTimeout) * time.Second
+	return c
+}
+
+func (c *Client) WithSignatureMethod(method string) *Client {
+	c.signMethod = method
+	return c
+}
+
+func (c *Client) WithHttpTransport(transport http.RoundTripper) *Client {
+	c.httpClient.Transport = transport
+	return c
+}
+
+func NewClientWithSecretId(secretId, secretKey, region string) (client *Client, err error) {
+	client = &Client{}
+	client.Init(region).WithSecretId(secretId, secretKey)
+	return
+}
diff --git a/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/credentials.go b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/credentials.go
new file mode 100644
index 0000000000000000000000000000000000000000..b734c137346e48ab121385703d8cd66a1183997c
--- /dev/null
+++ b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/credentials.go
@@ -0,0 +1,58 @@
+package common
+
+type Credential struct {
+	SecretId  string
+	SecretKey string
+	Token     string
+}
+
+func NewCredential(secretId, secretKey string) *Credential {
+	return &Credential{
+		SecretId:  secretId,
+		SecretKey: secretKey,
+	}
+}
+
+func NewTokenCredential(secretId, secretKey, token string) *Credential {
+	return &Credential{
+		SecretId:  secretId,
+		SecretKey: secretKey,
+		Token:     token,
+	}
+}
+
+func (c *Credential) GetCredentialParams() map[string]string {
+	p := map[string]string{
+		"SecretId": c.SecretId,
+	}
+	if c.Token != "" {
+		p["Token"] = c.Token
+	}
+	return p
+}
+
+// Nowhere use them and we haven't well designed these structures and
+// underlying method, which leads to the situation that it is hard to
+// refactor it to interfaces.
+// Hence they are removed and merged into Credential.
+
+//type TokenCredential struct {
+//	SecretId  string
+//	SecretKey string
+//	Token     string
+//}
+
+//func NewTokenCredential(secretId, secretKey, token string) *TokenCredential {
+//	return &TokenCredential{
+//		SecretId:  secretId,
+//		SecretKey: secretKey,
+//		Token:     token,
+//	}
+//}
+
+//func (c *TokenCredential) GetCredentialParams() map[string]string {
+//	return map[string]string{
+//		"SecretId": c.SecretId,
+//		"Token":    c.Token,
+//	}
+//}
diff --git a/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors/errors.go b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors/errors.go
new file mode 100644
index 0000000000000000000000000000000000000000..27589e59a1629482c106c099a1b706958c244246
--- /dev/null
+++ b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors/errors.go
@@ -0,0 +1,35 @@
+package errors
+
+import (
+	"fmt"
+)
+
+type TencentCloudSDKError struct {
+	Code      string
+	Message   string
+	RequestId string
+}
+
+func (e *TencentCloudSDKError) Error() string {
+	return fmt.Sprintf("[TencentCloudSDKError] Code=%s, Message=%s, RequestId=%s", e.Code, e.Message, e.RequestId)
+}
+
+func NewTencentCloudSDKError(code, message, requestId string) error {
+	return &TencentCloudSDKError{
+		Code:      code,
+		Message:   message,
+		RequestId: requestId,
+	}
+}
+
+func (e *TencentCloudSDKError) GetCode() string {
+	return e.Code
+}
+
+func (e *TencentCloudSDKError) GetMessage() string {
+	return e.Message
+}
+
+func (e *TencentCloudSDKError) GetRequestId() string {
+	return e.RequestId
+}
diff --git a/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http/request.go b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http/request.go
new file mode 100644
index 0000000000000000000000000000000000000000..c7912ad18feb54c8c638e2832e20eb2e13a3337d
--- /dev/null
+++ b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http/request.go
@@ -0,0 +1,233 @@
+package common
+
+import (
+	"io"
+	//"log"
+	"math/rand"
+	"net/url"
+	"reflect"
+	"strconv"
+	"strings"
+	"time"
+)
+
+const (
+	POST = "POST"
+	GET  = "GET"
+
+	RootDomain = "tencentcloudapi.com"
+	Path       = "/"
+)
+
+type Request interface {
+	GetAction() string
+	GetBodyReader() io.Reader
+	GetDomain() string
+	GetHttpMethod() string
+	GetParams() map[string]string
+	GetPath() string
+	GetService() string
+	GetUrl() string
+	GetVersion() string
+	SetDomain(string)
+	SetHttpMethod(string)
+}
+
+type BaseRequest struct {
+	httpMethod string
+	domain     string
+	path       string
+	params     map[string]string
+	formParams map[string]string
+
+	service string
+	version string
+	action  string
+}
+
+func (r *BaseRequest) GetAction() string {
+	return r.action
+}
+
+func (r *BaseRequest) GetHttpMethod() string {
+	return r.httpMethod
+}
+
+func (r *BaseRequest) GetParams() map[string]string {
+	return r.params
+}
+
+func (r *BaseRequest) GetPath() string {
+	return r.path
+}
+
+func (r *BaseRequest) GetDomain() string {
+	return r.domain
+}
+
+func (r *BaseRequest) SetDomain(domain string) {
+	r.domain = domain
+}
+
+func (r *BaseRequest) SetHttpMethod(method string) {
+	switch strings.ToUpper(method) {
+	case POST:
+		{
+			r.httpMethod = POST
+		}
+	case GET:
+		{
+			r.httpMethod = GET
+		}
+	default:
+		{
+			r.httpMethod = GET
+		}
+	}
+}
+
+func (r *BaseRequest) GetService() string {
+	return r.service
+}
+
+func (r *BaseRequest) GetUrl() string {
+	if r.httpMethod == GET {
+		return "https://" + r.domain + r.path + "?" + GetUrlQueriesEncoded(r.params)
+	} else if r.httpMethod == POST {
+		return "https://" + r.domain + r.path
+	} else {
+		return ""
+	}
+}
+
+func (r *BaseRequest) GetVersion() string {
+	return r.version
+}
+
+func GetUrlQueriesEncoded(params map[string]string) string {
+	values := url.Values{}
+	for key, value := range params {
+		if value != "" {
+			values.Add(key, value)
+		}
+	}
+	return values.Encode()
+}
+
+func (r *BaseRequest) GetBodyReader() io.Reader {
+	if r.httpMethod == POST {
+		s := GetUrlQueriesEncoded(r.params)
+		return strings.NewReader(s)
+	} else {
+		return strings.NewReader("")
+	}
+}
+
+func (r *BaseRequest) Init() *BaseRequest {
+	r.domain = ""
+	r.path = Path
+	r.params = make(map[string]string)
+	r.formParams = make(map[string]string)
+	return r
+}
+
+func (r *BaseRequest) WithApiInfo(service, version, action string) *BaseRequest {
+	r.service = service
+	r.version = version
+	r.action = action
+	return r
+}
+
+func GetServiceDomain(service string) (domain string) {
+	domain = service + "." + RootDomain
+	return
+}
+
+func CompleteCommonParams(request Request, region string) {
+	params := request.GetParams()
+	params["Region"] = region
+	if request.GetVersion() != "" {
+		params["Version"] = request.GetVersion()
+	}
+	params["Action"] = request.GetAction()
+	params["Timestamp"] = strconv.FormatInt(time.Now().Unix(), 10)
+	params["Nonce"] = strconv.Itoa(rand.Int())
+	params["RequestClient"] = "SDK_GO_3.0.82"
+}
+
+func ConstructParams(req Request) (err error) {
+	value := reflect.ValueOf(req).Elem()
+	err = flatStructure(value, req, "")
+	//log.Printf("[DEBUG] params=%s", req.GetParams())
+	return
+}
+
+func flatStructure(value reflect.Value, request Request, prefix string) (err error) {
+	//log.Printf("[DEBUG] reflect value: %v", value.Type())
+	valueType := value.Type()
+	for i := 0; i < valueType.NumField(); i++ {
+		tag := valueType.Field(i).Tag
+		nameTag, hasNameTag := tag.Lookup("name")
+		if !hasNameTag {
+			continue
+		}
+		field := value.Field(i)
+		kind := field.Kind()
+		if kind == reflect.Ptr && field.IsNil() {
+			continue
+		}
+		if kind == reflect.Ptr {
+			field = field.Elem()
+			kind = field.Kind()
+		}
+		key := prefix + nameTag
+		if kind == reflect.String {
+			s := field.String()
+			if s != "" {
+				request.GetParams()[key] = s
+			}
+		} else if kind == reflect.Bool {
+			request.GetParams()[key] = strconv.FormatBool(field.Bool())
+		} else if kind == reflect.Int || kind == reflect.Int64 {
+			request.GetParams()[key] = strconv.FormatInt(field.Int(), 10)
+		} else if kind == reflect.Uint || kind == reflect.Uint64 {
+			request.GetParams()[key] = strconv.FormatUint(field.Uint(), 10)
+		} else if kind == reflect.Float64 {
+			request.GetParams()[key] = strconv.FormatFloat(field.Float(), 'f', -1, 64)
+		} else if kind == reflect.Slice {
+			list := value.Field(i)
+			for j := 0; j < list.Len(); j++ {
+				vj := list.Index(j)
+				key := prefix + nameTag + "." + strconv.Itoa(j)
+				kind = vj.Kind()
+				if kind == reflect.Ptr && vj.IsNil() {
+					continue
+				}
+				if kind == reflect.Ptr {
+					vj = vj.Elem()
+					kind = vj.Kind()
+				}
+				if kind == reflect.String {
+					request.GetParams()[key] = vj.String()
+				} else if kind == reflect.Bool {
+					request.GetParams()[key] = strconv.FormatBool(vj.Bool())
+				} else if kind == reflect.Int || kind == reflect.Int64 {
+					request.GetParams()[key] = strconv.FormatInt(vj.Int(), 10)
+				} else if kind == reflect.Uint || kind == reflect.Uint64 {
+					request.GetParams()[key] = strconv.FormatUint(vj.Uint(), 10)
+				} else if kind == reflect.Float64 {
+					request.GetParams()[key] = strconv.FormatFloat(vj.Float(), 'f', -1, 64)
+				} else {
+					if err = flatStructure(vj, request, key+"."); err != nil {
+						return
+					}
+				}
+			}
+		} else {
+			if err = flatStructure(reflect.ValueOf(field.Interface()), request, prefix+nameTag+"."); err != nil {
+				return
+			}
+		}
+	}
+	return
+}
diff --git a/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http/response.go b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http/response.go
new file mode 100644
index 0000000000000000000000000000000000000000..288f21bdf65ba47fd8070688c3c54666917f2526
--- /dev/null
+++ b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http/response.go
@@ -0,0 +1,81 @@
+package common
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	//"log"
+	"net/http"
+
+	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
+)
+
+type Response interface {
+	ParseErrorFromHTTPResponse(body []byte) error
+}
+
+type BaseResponse struct {
+}
+
+type ErrorResponse struct {
+	Response struct {
+		Error struct {
+			Code    string `json:"Code"`
+			Message string `json:"Message"`
+		} `json:"Error" omitempty`
+		RequestId string `json:"RequestId"`
+	} `json:"Response"`
+}
+
+type DeprecatedAPIErrorResponse struct {
+	Code     int    `json:"code"`
+	Message  string `json:"message"`
+	CodeDesc string `json:"codeDesc"`
+}
+
+func (r *BaseResponse) ParseErrorFromHTTPResponse(body []byte) (err error) {
+	resp := &ErrorResponse{}
+	err = json.Unmarshal(body, resp)
+	if err != nil {
+		msg := fmt.Sprintf("Fail to parse json content: %s, because: %s", body, err)
+		return errors.NewTencentCloudSDKError("ClientError.ParseJsonError", msg, "")
+	}
+	if resp.Response.Error.Code != "" {
+		return errors.NewTencentCloudSDKError(resp.Response.Error.Code, resp.Response.Error.Message, resp.Response.RequestId)
+	}
+
+	deprecated := &DeprecatedAPIErrorResponse{}
+	err = json.Unmarshal(body, deprecated)
+	if err != nil {
+		msg := fmt.Sprintf("Fail to parse json content: %s, because: %s", body, err)
+		return errors.NewTencentCloudSDKError("ClientError.ParseJsonError", msg, "")
+	}
+	if deprecated.Code != 0 {
+		return errors.NewTencentCloudSDKError(deprecated.CodeDesc, deprecated.Message, "")
+	}
+	return nil
+}
+
+func ParseFromHttpResponse(hr *http.Response, response Response) (err error) {
+	defer hr.Body.Close()
+	body, err := ioutil.ReadAll(hr.Body)
+	if err != nil {
+		msg := fmt.Sprintf("Fail to read response body because %s", err)
+		return errors.NewTencentCloudSDKError("ClientError.IOError", msg, "")
+	}
+	if hr.StatusCode != 200 {
+		msg := fmt.Sprintf("Request fail with http status code: %s, with body: %s", hr.Status, body)
+		return errors.NewTencentCloudSDKError("ClientError.HttpStatusCodeError", msg, "")
+	}
+	//log.Printf("[DEBUG] Response Body=%s", body)
+	err = response.ParseErrorFromHTTPResponse(body)
+	if err != nil {
+		return
+	}
+	err = json.Unmarshal(body, &response)
+	if err != nil {
+		msg := fmt.Sprintf("Fail to parse json content: %s, because: %s", body, err)
+		return errors.NewTencentCloudSDKError("ClientError.ParseJsonError", msg, "")
+	}
+	return
+}
diff --git a/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile/client_profile.go b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile/client_profile.go
new file mode 100644
index 0000000000000000000000000000000000000000..21069ff99de7d669fe70a066d628050a18d9302f
--- /dev/null
+++ b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile/client_profile.go
@@ -0,0 +1,21 @@
+package profile
+
+type ClientProfile struct {
+	HttpProfile *HttpProfile
+	// Valid choices: HmacSHA1, HmacSHA256, TC3-HMAC-SHA256.
+	// Default value is TC3-HMAC-SHA256.
+	SignMethod      string
+	UnsignedPayload bool
+	// Valid choices: zh-CN, en-US.
+	// Default value is zh-CN.
+	Language string
+}
+
+func NewClientProfile() *ClientProfile {
+	return &ClientProfile{
+		HttpProfile:     NewHttpProfile(),
+		SignMethod:      "TC3-HMAC-SHA256",
+		UnsignedPayload: false,
+		Language:        "zh-CN",
+	}
+}
diff --git a/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile/http_profile.go b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile/http_profile.go
new file mode 100644
index 0000000000000000000000000000000000000000..8d4bf8f57b89f8bbde6f95b31b6c5721df06be10
--- /dev/null
+++ b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile/http_profile.go
@@ -0,0 +1,17 @@
+package profile
+
+type HttpProfile struct {
+	ReqMethod  string
+	ReqTimeout int
+	Endpoint   string
+	Protocol   string
+}
+
+func NewHttpProfile() *HttpProfile {
+	return &HttpProfile{
+		ReqMethod:  "POST",
+		ReqTimeout: 60,
+		Endpoint:   "",
+		Protocol:   "HTTPS",
+	}
+}
diff --git a/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/sign.go b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/sign.go
new file mode 100644
index 0000000000000000000000000000000000000000..0aa7b7355719d0f07c54bc41e90aa924d1e13536
--- /dev/null
+++ b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/sign.go
@@ -0,0 +1,94 @@
+package common
+
+import (
+	"bytes"
+	"crypto/hmac"
+	"crypto/sha1"
+	"crypto/sha256"
+	"encoding/base64"
+	"encoding/hex"
+	"sort"
+
+	tchttp "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http"
+)
+
+const (
+	SHA256 = "HmacSHA256"
+	SHA1   = "HmacSHA1"
+)
+
+func Sign(s, secretKey, method string) string {
+	hashed := hmac.New(sha1.New, []byte(secretKey))
+	if method == SHA256 {
+		hashed = hmac.New(sha256.New, []byte(secretKey))
+	}
+	hashed.Write([]byte(s))
+
+	return base64.StdEncoding.EncodeToString(hashed.Sum(nil))
+}
+
+func sha256hex(s string) string {
+	b := sha256.Sum256([]byte(s))
+	return hex.EncodeToString(b[:])
+}
+
+func hmacsha256(s, key string) string {
+	hashed := hmac.New(sha256.New, []byte(key))
+	hashed.Write([]byte(s))
+	return string(hashed.Sum(nil))
+}
+
+func signRequest(request tchttp.Request, credential *Credential, method string) (err error) {
+	if method != SHA256 {
+		method = SHA1
+	}
+	checkAuthParams(request, credential, method)
+	s := getStringToSign(request)
+	signature := Sign(s, credential.SecretKey, method)
+	request.GetParams()["Signature"] = signature
+	return
+}
+
+func checkAuthParams(request tchttp.Request, credential *Credential, method string) {
+	params := request.GetParams()
+	credentialParams := credential.GetCredentialParams()
+	for key, value := range credentialParams {
+		params[key] = value
+	}
+	params["SignatureMethod"] = method
+	delete(params, "Signature")
+}
+
+func getStringToSign(request tchttp.Request) string {
+	method := request.GetHttpMethod()
+	domain := request.GetDomain()
+	path := request.GetPath()
+
+	var buf bytes.Buffer
+	buf.WriteString(method)
+	buf.WriteString(domain)
+	buf.WriteString(path)
+	buf.WriteString("?")
+
+	params := request.GetParams()
+	// sort params
+	keys := make([]string, 0, len(params))
+	for k, _ := range params {
+		keys = append(keys, k)
+	}
+	sort.Strings(keys)
+
+	for i := range keys {
+		k := keys[i]
+		// TODO: check if server side allows empty value in url.
+		if params[k] == "" {
+			continue
+		}
+		buf.WriteString(k)
+		buf.WriteString("=")
+		buf.WriteString(params[k])
+		buf.WriteString("&")
+	}
+	buf.Truncate(buf.Len() - 1)
+	return buf.String()
+}
diff --git a/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/types.go b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/types.go
new file mode 100644
index 0000000000000000000000000000000000000000..ec2c786dbf3d1acb8faf64e15e49966d0a85c5c4
--- /dev/null
+++ b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/types.go
@@ -0,0 +1,47 @@
+package common
+
+func IntPtr(v int) *int {
+	return &v
+}
+
+func Int64Ptr(v int64) *int64 {
+	return &v
+}
+
+func UintPtr(v uint) *uint {
+	return &v
+}
+
+func Uint64Ptr(v uint64) *uint64 {
+	return &v
+}
+
+func Float64Ptr(v float64) *float64 {
+	return &v
+}
+
+func StringPtr(v string) *string {
+	return &v
+}
+
+func StringValues(ptrs []*string) []string {
+	values := make([]string, len(ptrs))
+	for i := 0; i < len(ptrs); i++ {
+		if ptrs[i] != nil {
+			values[i] = *ptrs[i]
+		}
+	}
+	return values
+}
+
+func StringPtrs(vals []string) []*string {
+	ptrs := make([]*string, len(vals))
+	for i := 0; i < len(vals); i++ {
+		ptrs[i] = &vals[i]
+	}
+	return ptrs
+}
+
+func BoolPtr(v bool) *bool {
+	return &v
+}
diff --git a/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tag/v20180813/client.go b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tag/v20180813/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..4980f915d67f779cd1a061c220c5faf48b349342
--- /dev/null
+++ b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tag/v20180813/client.go
@@ -0,0 +1,294 @@
+// Copyright (c) 2017-2018 THL A29 Limited, a Tencent company. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package v20180813
+
+import (
+    "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
+    tchttp "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http"
+    "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
+)
+
+const APIVersion = "2018-08-13"
+
+type Client struct {
+    common.Client
+}
+
+// Deprecated
+func NewClientWithSecretId(secretId, secretKey, region string) (client *Client, err error) {
+    cpf := profile.NewClientProfile()
+    client = &Client{}
+    client.Init(region).WithSecretId(secretId, secretKey).WithProfile(cpf)
+    return
+}
+
+func NewClient(credential *common.Credential, region string, clientProfile *profile.ClientProfile) (client *Client, err error) {
+    client = &Client{}
+    client.Init(region).
+        WithCredential(credential).
+        WithProfile(clientProfile)
+    return
+}
+
+
+func NewAddResourceTagRequest() (request *AddResourceTagRequest) {
+    request = &AddResourceTagRequest{
+        BaseRequest: &tchttp.BaseRequest{},
+    }
+    request.Init().WithApiInfo("tag", APIVersion, "AddResourceTag")
+    return
+}
+
+func NewAddResourceTagResponse() (response *AddResourceTagResponse) {
+    response = &AddResourceTagResponse{
+        BaseResponse: &tchttp.BaseResponse{},
+    }
+    return
+}
+
+// 本接口用于给标签关联资源
+func (c *Client) AddResourceTag(request *AddResourceTagRequest) (response *AddResourceTagResponse, err error) {
+    if request == nil {
+        request = NewAddResourceTagRequest()
+    }
+    response = NewAddResourceTagResponse()
+    err = c.Send(request, response)
+    return
+}
+
+func NewCreateTagRequest() (request *CreateTagRequest) {
+    request = &CreateTagRequest{
+        BaseRequest: &tchttp.BaseRequest{},
+    }
+    request.Init().WithApiInfo("tag", APIVersion, "CreateTag")
+    return
+}
+
+func NewCreateTagResponse() (response *CreateTagResponse) {
+    response = &CreateTagResponse{
+        BaseResponse: &tchttp.BaseResponse{},
+    }
+    return
+}
+
+// 本接口用于创建一对标签键和标签值
+func (c *Client) CreateTag(request *CreateTagRequest) (response *CreateTagResponse, err error) {
+    if request == nil {
+        request = NewCreateTagRequest()
+    }
+    response = NewCreateTagResponse()
+    err = c.Send(request, response)
+    return
+}
+
+func NewDeleteResourceTagRequest() (request *DeleteResourceTagRequest) {
+    request = &DeleteResourceTagRequest{
+        BaseRequest: &tchttp.BaseRequest{},
+    }
+    request.Init().WithApiInfo("tag", APIVersion, "DeleteResourceTag")
+    return
+}
+
+func NewDeleteResourceTagResponse() (response *DeleteResourceTagResponse) {
+    response = &DeleteResourceTagResponse{
+        BaseResponse: &tchttp.BaseResponse{},
+    }
+    return
+}
+
+// 本接口用于解除标签和资源的关联关系
+func (c *Client) DeleteResourceTag(request *DeleteResourceTagRequest) (response *DeleteResourceTagResponse, err error) {
+    if request == nil {
+        request = NewDeleteResourceTagRequest()
+    }
+    response = NewDeleteResourceTagResponse()
+    err = c.Send(request, response)
+    return
+}
+
+func NewDeleteTagRequest() (request *DeleteTagRequest) {
+    request = &DeleteTagRequest{
+        BaseRequest: &tchttp.BaseRequest{},
+    }
+    request.Init().WithApiInfo("tag", APIVersion, "DeleteTag")
+    return
+}
+
+func NewDeleteTagResponse() (response *DeleteTagResponse) {
+    response = &DeleteTagResponse{
+        BaseResponse: &tchttp.BaseResponse{},
+    }
+    return
+}
+
+// 本接口用于删除一对标签键和标签值
+func (c *Client) DeleteTag(request *DeleteTagRequest) (response *DeleteTagResponse, err error) {
+    if request == nil {
+        request = NewDeleteTagRequest()
+    }
+    response = NewDeleteTagResponse()
+    err = c.Send(request, response)
+    return
+}
+
+func NewDescribeResourceTagsByResourceIdsRequest() (request *DescribeResourceTagsByResourceIdsRequest) {
+    request = &DescribeResourceTagsByResourceIdsRequest{
+        BaseRequest: &tchttp.BaseRequest{},
+    }
+    request.Init().WithApiInfo("tag", APIVersion, "DescribeResourceTagsByResourceIds")
+    return
+}
+
+func NewDescribeResourceTagsByResourceIdsResponse() (response *DescribeResourceTagsByResourceIdsResponse) {
+    response = &DescribeResourceTagsByResourceIdsResponse{
+        BaseResponse: &tchttp.BaseResponse{},
+    }
+    return
+}
+
+// 用于查询已有资源标签键值对
+func (c *Client) DescribeResourceTagsByResourceIds(request *DescribeResourceTagsByResourceIdsRequest) (response *DescribeResourceTagsByResourceIdsResponse, err error) {
+    if request == nil {
+        request = NewDescribeResourceTagsByResourceIdsRequest()
+    }
+    response = NewDescribeResourceTagsByResourceIdsResponse()
+    err = c.Send(request, response)
+    return
+}
+
+func NewDescribeTagKeysRequest() (request *DescribeTagKeysRequest) {
+    request = &DescribeTagKeysRequest{
+        BaseRequest: &tchttp.BaseRequest{},
+    }
+    request.Init().WithApiInfo("tag", APIVersion, "DescribeTagKeys")
+    return
+}
+
+func NewDescribeTagKeysResponse() (response *DescribeTagKeysResponse) {
+    response = &DescribeTagKeysResponse{
+        BaseResponse: &tchttp.BaseResponse{},
+    }
+    return
+}
+
+// 用于查询已建立的标签列表中的标签键。
+func (c *Client) DescribeTagKeys(request *DescribeTagKeysRequest) (response *DescribeTagKeysResponse, err error) {
+    if request == nil {
+        request = NewDescribeTagKeysRequest()
+    }
+    response = NewDescribeTagKeysResponse()
+    err = c.Send(request, response)
+    return
+}
+
+func NewDescribeTagValuesRequest() (request *DescribeTagValuesRequest) {
+    request = &DescribeTagValuesRequest{
+        BaseRequest: &tchttp.BaseRequest{},
+    }
+    request.Init().WithApiInfo("tag", APIVersion, "DescribeTagValues")
+    return
+}
+
+func NewDescribeTagValuesResponse() (response *DescribeTagValuesResponse) {
+    response = &DescribeTagValuesResponse{
+        BaseResponse: &tchttp.BaseResponse{},
+    }
+    return
+}
+
+// 用于查询已建立的标签列表中的标签值。
+func (c *Client) DescribeTagValues(request *DescribeTagValuesRequest) (response *DescribeTagValuesResponse, err error) {
+    if request == nil {
+        request = NewDescribeTagValuesRequest()
+    }
+    response = NewDescribeTagValuesResponse()
+    err = c.Send(request, response)
+    return
+}
+
+func NewDescribeTagsRequest() (request *DescribeTagsRequest) {
+    request = &DescribeTagsRequest{
+        BaseRequest: &tchttp.BaseRequest{},
+    }
+    request.Init().WithApiInfo("tag", APIVersion, "DescribeTags")
+    return
+}
+
+func NewDescribeTagsResponse() (response *DescribeTagsResponse) {
+    response = &DescribeTagsResponse{
+        BaseResponse: &tchttp.BaseResponse{},
+    }
+    return
+}
+
+// 用于查询已建立的标签列表。
+func (c *Client) DescribeTags(request *DescribeTagsRequest) (response *DescribeTagsResponse, err error) {
+    if request == nil {
+        request = NewDescribeTagsRequest()
+    }
+    response = NewDescribeTagsResponse()
+    err = c.Send(request, response)
+    return
+}
+
+func NewModifyResourceTagsRequest() (request *ModifyResourceTagsRequest) {
+    request = &ModifyResourceTagsRequest{
+        BaseRequest: &tchttp.BaseRequest{},
+    }
+    request.Init().WithApiInfo("tag", APIVersion, "ModifyResourceTags")
+    return
+}
+
+func NewModifyResourceTagsResponse() (response *ModifyResourceTagsResponse) {
+    response = &ModifyResourceTagsResponse{
+        BaseResponse: &tchttp.BaseResponse{},
+    }
+    return
+}
+
+// 本接口用于修改资源关联的所有标签
+func (c *Client) ModifyResourceTags(request *ModifyResourceTagsRequest) (response *ModifyResourceTagsResponse, err error) {
+    if request == nil {
+        request = NewModifyResourceTagsRequest()
+    }
+    response = NewModifyResourceTagsResponse()
+    err = c.Send(request, response)
+    return
+}
+
+func NewUpdateResourceTagValueRequest() (request *UpdateResourceTagValueRequest) {
+    request = &UpdateResourceTagValueRequest{
+        BaseRequest: &tchttp.BaseRequest{},
+    }
+    request.Init().WithApiInfo("tag", APIVersion, "UpdateResourceTagValue")
+    return
+}
+
+func NewUpdateResourceTagValueResponse() (response *UpdateResourceTagValueResponse) {
+    response = &UpdateResourceTagValueResponse{
+        BaseResponse: &tchttp.BaseResponse{},
+    }
+    return
+}
+
+// 本接口用于修改资源已关联的标签值(标签键不变)
+func (c *Client) UpdateResourceTagValue(request *UpdateResourceTagValueRequest) (response *UpdateResourceTagValueResponse, err error) {
+    if request == nil {
+        request = NewUpdateResourceTagValueRequest()
+    }
+    response = NewUpdateResourceTagValueResponse()
+    err = c.Send(request, response)
+    return
+}
diff --git a/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tag/v20180813/models.go b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tag/v20180813/models.go
new file mode 100644
index 0000000000000000000000000000000000000000..b326cddbe451e36f8ac0a04885d98d35e2217b12
--- /dev/null
+++ b/vendor/github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tag/v20180813/models.go
@@ -0,0 +1,523 @@
+// Copyright (c) 2017-2018 THL A29 Limited, a Tencent company. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package v20180813
+
+import (
+    "encoding/json"
+
+    tchttp "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http"
+)
+
+type AddResourceTagRequest struct {
+	*tchttp.BaseRequest
+
+	// 标签键
+	TagKey *string `json:"TagKey,omitempty" name:"TagKey"`
+
+	// 标签值
+	TagValue *string `json:"TagValue,omitempty" name:"TagValue"`
+
+	// 资源六段式描述
+	Resource *string `json:"Resource,omitempty" name:"Resource"`
+}
+
+func (r *AddResourceTagRequest) ToJsonString() string {
+    b, _ := json.Marshal(r)
+    return string(b)
+}
+
+func (r *AddResourceTagRequest) FromJsonString(s string) error {
+    return json.Unmarshal([]byte(s), &r)
+}
+
+type AddResourceTagResponse struct {
+	*tchttp.BaseResponse
+	Response *struct {
+
+		// 唯一请求 ID,每次请求都会返回。定位问题时需要提供该次请求的 RequestId。
+		RequestId *string `json:"RequestId,omitempty" name:"RequestId"`
+	} `json:"Response"`
+}
+
+func (r *AddResourceTagResponse) ToJsonString() string {
+    b, _ := json.Marshal(r)
+    return string(b)
+}
+
+func (r *AddResourceTagResponse) FromJsonString(s string) error {
+    return json.Unmarshal([]byte(s), &r)
+}
+
+type CreateTagRequest struct {
+	*tchttp.BaseRequest
+
+	// 标签键
+	TagKey *string `json:"TagKey,omitempty" name:"TagKey"`
+
+	// 标签值
+	TagValue *string `json:"TagValue,omitempty" name:"TagValue"`
+}
+
+func (r *CreateTagRequest) ToJsonString() string {
+    b, _ := json.Marshal(r)
+    return string(b)
+}
+
+func (r *CreateTagRequest) FromJsonString(s string) error {
+    return json.Unmarshal([]byte(s), &r)
+}
+
+type CreateTagResponse struct {
+	*tchttp.BaseResponse
+	Response *struct {
+
+		// 唯一请求 ID,每次请求都会返回。定位问题时需要提供该次请求的 RequestId。
+		RequestId *string `json:"RequestId,omitempty" name:"RequestId"`
+	} `json:"Response"`
+}
+
+func (r *CreateTagResponse) ToJsonString() string {
+    b, _ := json.Marshal(r)
+    return string(b)
+}
+
+func (r *CreateTagResponse) FromJsonString(s string) error {
+    return json.Unmarshal([]byte(s), &r)
+}
+
+type DeleteResourceTagRequest struct {
+	*tchttp.BaseRequest
+
+	// 标签键
+	TagKey *string `json:"TagKey,omitempty" name:"TagKey"`
+
+	// 资源六段式描述
+	Resource *string `json:"Resource,omitempty" name:"Resource"`
+}
+
+func (r *DeleteResourceTagRequest) ToJsonString() string {
+    b, _ := json.Marshal(r)
+    return string(b)
+}
+
+func (r *DeleteResourceTagRequest) FromJsonString(s string) error {
+    return json.Unmarshal([]byte(s), &r)
+}
+
+type DeleteResourceTagResponse struct {
+	*tchttp.BaseResponse
+	Response *struct {
+
+		// 唯一请求 ID,每次请求都会返回。定位问题时需要提供该次请求的 RequestId。
+		RequestId *string `json:"RequestId,omitempty" name:"RequestId"`
+	} `json:"Response"`
+}
+
+func (r *DeleteResourceTagResponse) ToJsonString() string {
+    b, _ := json.Marshal(r)
+    return string(b)
+}
+
+func (r *DeleteResourceTagResponse) FromJsonString(s string) error {
+    return json.Unmarshal([]byte(s), &r)
+}
+
+type DeleteTagRequest struct {
+	*tchttp.BaseRequest
+
+	// 需要删除的标签键
+	TagKey *string `json:"TagKey,omitempty" name:"TagKey"`
+
+	// 需要删除的标签值
+	TagValue *string `json:"TagValue,omitempty" name:"TagValue"`
+}
+
+func (r *DeleteTagRequest) ToJsonString() string {
+    b, _ := json.Marshal(r)
+    return string(b)
+}
+
+func (r *DeleteTagRequest) FromJsonString(s string) error {
+    return json.Unmarshal([]byte(s), &r)
+}
+
+type DeleteTagResponse struct {
+	*tchttp.BaseResponse
+	Response *struct {
+
+		// 唯一请求 ID,每次请求都会返回。定位问题时需要提供该次请求的 RequestId。
+		RequestId *string `json:"RequestId,omitempty" name:"RequestId"`
+	} `json:"Response"`
+}
+
+func (r *DeleteTagResponse) ToJsonString() string {
+    b, _ := json.Marshal(r)
+    return string(b)
+}
+
+func (r *DeleteTagResponse) FromJsonString(s string) error {
+    return json.Unmarshal([]byte(s), &r)
+}
+
+type DescribeResourceTagsByResourceIdsRequest struct {
+	*tchttp.BaseRequest
+
+	// 业务类型
+	ServiceType *string `json:"ServiceType,omitempty" name:"ServiceType"`
+
+	// 资源前缀
+	ResourcePrefix *string `json:"ResourcePrefix,omitempty" name:"ResourcePrefix"`
+
+	// 资源唯一标记
+	ResourceIds []*string `json:"ResourceIds,omitempty" name:"ResourceIds" list`
+
+	// 资源所在地域
+	ResourceRegion *string `json:"ResourceRegion,omitempty" name:"ResourceRegion"`
+
+	// 数据偏移量,默认为 0, 必须为Limit参数的整数倍
+	Offset *uint64 `json:"Offset,omitempty" name:"Offset"`
+
+	// 每页大小,默认为 15
+	Limit *uint64 `json:"Limit,omitempty" name:"Limit"`
+}
+
+func (r *DescribeResourceTagsByResourceIdsRequest) ToJsonString() string {
+    b, _ := json.Marshal(r)
+    return string(b)
+}
+
+func (r *DescribeResourceTagsByResourceIdsRequest) FromJsonString(s string) error {
+    return json.Unmarshal([]byte(s), &r)
+}
+
+type DescribeResourceTagsByResourceIdsResponse struct {
+	*tchttp.BaseResponse
+	Response *struct {
+
+		// 结果总数
+		TotalCount *uint64 `json:"TotalCount,omitempty" name:"TotalCount"`
+
+		// 数据位移偏量
+		Offset *uint64 `json:"Offset,omitempty" name:"Offset"`
+
+		// 每页大小
+		Limit *uint64 `json:"Limit,omitempty" name:"Limit"`
+
+		// 标签列表
+		Tags []*TagResource `json:"Tags,omitempty" name:"Tags" list`
+
+		// 唯一请求 ID,每次请求都会返回。定位问题时需要提供该次请求的 RequestId。
+		RequestId *string `json:"RequestId,omitempty" name:"RequestId"`
+	} `json:"Response"`
+}
+
+func (r *DescribeResourceTagsByResourceIdsResponse) ToJsonString() string {
+    b, _ := json.Marshal(r)
+    return string(b)
+}
+
+func (r *DescribeResourceTagsByResourceIdsResponse) FromJsonString(s string) error {
+    return json.Unmarshal([]byte(s), &r)
+}
+
+type DescribeTagKeysRequest struct {
+	*tchttp.BaseRequest
+
+	// 创建者用户 Uin,不传或为空只将 Uin 作为条件查询
+	CreateUin *uint64 `json:"CreateUin,omitempty" name:"CreateUin"`
+
+	// 数据偏移量,默认为 0, 必须为Limit参数的整数倍
+	Offset *uint64 `json:"Offset,omitempty" name:"Offset"`
+
+	// 每页大小,默认为 15
+	Limit *uint64 `json:"Limit,omitempty" name:"Limit"`
+}
+
+func (r *DescribeTagKeysRequest) ToJsonString() string {
+    b, _ := json.Marshal(r)
+    return string(b)
+}
+
+func (r *DescribeTagKeysRequest) FromJsonString(s string) error {
+    return json.Unmarshal([]byte(s), &r)
+}
+
+type DescribeTagKeysResponse struct {
+	*tchttp.BaseResponse
+	Response *struct {
+
+		// 结果总数
+		TotalCount *uint64 `json:"TotalCount,omitempty" name:"TotalCount"`
+
+		// 数据位移偏量
+		Offset *uint64 `json:"Offset,omitempty" name:"Offset"`
+
+		// 每页大小
+		Limit *uint64 `json:"Limit,omitempty" name:"Limit"`
+
+		// 标签列表
+		Tags []*string `json:"Tags,omitempty" name:"Tags" list`
+
+		// 唯一请求 ID,每次请求都会返回。定位问题时需要提供该次请求的 RequestId。
+		RequestId *string `json:"RequestId,omitempty" name:"RequestId"`
+	} `json:"Response"`
+}
+
+func (r *DescribeTagKeysResponse) ToJsonString() string {
+    b, _ := json.Marshal(r)
+    return string(b)
+}
+
+func (r *DescribeTagKeysResponse) FromJsonString(s string) error {
+    return json.Unmarshal([]byte(s), &r)
+}
+
+type DescribeTagValuesRequest struct {
+	*tchttp.BaseRequest
+
+	// 标签键列表
+	TagKeys []*string `json:"TagKeys,omitempty" name:"TagKeys" list`
+
+	// 创建者用户 Uin,不传或为空只将 Uin 作为条件查询
+	CreateUin *uint64 `json:"CreateUin,omitempty" name:"CreateUin"`
+
+	// 数据偏移量,默认为 0, 必须为Limit参数的整数倍
+	Offset *uint64 `json:"Offset,omitempty" name:"Offset"`
+
+	// 每页大小,默认为 15
+	Limit *uint64 `json:"Limit,omitempty" name:"Limit"`
+}
+
+func (r *DescribeTagValuesRequest) ToJsonString() string {
+    b, _ := json.Marshal(r)
+    return string(b)
+}
+
+func (r *DescribeTagValuesRequest) FromJsonString(s string) error {
+    return json.Unmarshal([]byte(s), &r)
+}
+
+type DescribeTagValuesResponse struct {
+	*tchttp.BaseResponse
+	Response *struct {
+
+		// 结果总数
+		TotalCount *uint64 `json:"TotalCount,omitempty" name:"TotalCount"`
+
+		// 数据位移偏量
+		Offset *uint64 `json:"Offset,omitempty" name:"Offset"`
+
+		// 每页大小
+		Limit *uint64 `json:"Limit,omitempty" name:"Limit"`
+
+		// 标签列表
+		Tags []*Tag `json:"Tags,omitempty" name:"Tags" list`
+
+		// 唯一请求 ID,每次请求都会返回。定位问题时需要提供该次请求的 RequestId。
+		RequestId *string `json:"RequestId,omitempty" name:"RequestId"`
+	} `json:"Response"`
+}
+
+func (r *DescribeTagValuesResponse) ToJsonString() string {
+    b, _ := json.Marshal(r)
+    return string(b)
+}
+
+func (r *DescribeTagValuesResponse) FromJsonString(s string) error {
+    return json.Unmarshal([]byte(s), &r)
+}
+
+type DescribeTagsRequest struct {
+	*tchttp.BaseRequest
+
+	// 标签键,与标签值同时存在或同时不存在,不存在时表示查询该用户所有标签
+	TagKey *string `json:"TagKey,omitempty" name:"TagKey"`
+
+	// 标签值,与标签键同时存在或同时不存在,不存在时表示查询该用户所有标签
+	TagValue *string `json:"TagValue,omitempty" name:"TagValue"`
+
+	// 数据偏移量,默认为 0, 必须为Limit参数的整数倍
+	Offset *uint64 `json:"Offset,omitempty" name:"Offset"`
+
+	// 每页大小,默认为 15
+	Limit *uint64 `json:"Limit,omitempty" name:"Limit"`
+
+	// 创建者用户 Uin,不传或为空只将 Uin 作为条件查询
+	CreateUin *uint64 `json:"CreateUin,omitempty" name:"CreateUin"`
+}
+
+func (r *DescribeTagsRequest) ToJsonString() string {
+    b, _ := json.Marshal(r)
+    return string(b)
+}
+
+func (r *DescribeTagsRequest) FromJsonString(s string) error {
+    return json.Unmarshal([]byte(s), &r)
+}
+
+type DescribeTagsResponse struct {
+	*tchttp.BaseResponse
+	Response *struct {
+
+		// 结果总数
+		TotalCount *uint64 `json:"TotalCount,omitempty" name:"TotalCount"`
+
+		// 数据位移偏量
+		Offset *uint64 `json:"Offset,omitempty" name:"Offset"`
+
+		// 每页大小
+		Limit *uint64 `json:"Limit,omitempty" name:"Limit"`
+
+		// 标签列表
+		Tags []*TagWithDelete `json:"Tags,omitempty" name:"Tags" list`
+
+		// 唯一请求 ID,每次请求都会返回。定位问题时需要提供该次请求的 RequestId。
+		RequestId *string `json:"RequestId,omitempty" name:"RequestId"`
+	} `json:"Response"`
+}
+
+func (r *DescribeTagsResponse) ToJsonString() string {
+    b, _ := json.Marshal(r)
+    return string(b)
+}
+
+func (r *DescribeTagsResponse) FromJsonString(s string) error {
+    return json.Unmarshal([]byte(s), &r)
+}
+
+type ModifyResourceTagsRequest struct {
+	*tchttp.BaseRequest
+
+	// 资源的六段式描述
+	Resource *string `json:"Resource,omitempty" name:"Resource"`
+
+	// 需要增加或修改的标签集合。如果Resource描述的资源未关联输入的标签键,则增加关联;若已关联,则将该资源关联的键对应的标签值修改为输入值。本接口中ReplaceTags和DeleteTags二者必须存在其一,且二者不能包含相同的标签键
+	ReplaceTags []*Tag `json:"ReplaceTags,omitempty" name:"ReplaceTags" list`
+
+	// 需要解关联的标签集合。本接口中ReplaceTags和DeleteTags二者必须存在其一,且二者不能包含相同的标签键
+	DeleteTags []*TagKeyObject `json:"DeleteTags,omitempty" name:"DeleteTags" list`
+}
+
+func (r *ModifyResourceTagsRequest) ToJsonString() string {
+    b, _ := json.Marshal(r)
+    return string(b)
+}
+
+func (r *ModifyResourceTagsRequest) FromJsonString(s string) error {
+    return json.Unmarshal([]byte(s), &r)
+}
+
+type ModifyResourceTagsResponse struct {
+	*tchttp.BaseResponse
+	Response *struct {
+
+		// 唯一请求 ID,每次请求都会返回。定位问题时需要提供该次请求的 RequestId。
+		RequestId *string `json:"RequestId,omitempty" name:"RequestId"`
+	} `json:"Response"`
+}
+
+func (r *ModifyResourceTagsResponse) ToJsonString() string {
+    b, _ := json.Marshal(r)
+    return string(b)
+}
+
+func (r *ModifyResourceTagsResponse) FromJsonString(s string) error {
+    return json.Unmarshal([]byte(s), &r)
+}
+
+type Tag struct {
+
+	// 标签键
+	TagKey *string `json:"TagKey,omitempty" name:"TagKey"`
+
+	// 标签值
+	TagValue *string `json:"TagValue,omitempty" name:"TagValue"`
+}
+
+type TagKeyObject struct {
+
+	// 标签键
+	TagKey *string `json:"TagKey,omitempty" name:"TagKey"`
+}
+
+type TagResource struct {
+
+	// 标签键
+	TagKey *string `json:"TagKey,omitempty" name:"TagKey"`
+
+	// 标签值
+	TagValue *string `json:"TagValue,omitempty" name:"TagValue"`
+
+	// 资源ID
+	ResourceId *string `json:"ResourceId,omitempty" name:"ResourceId"`
+
+	// 标签键MD5值
+	TagKeyMd5 *string `json:"TagKeyMd5,omitempty" name:"TagKeyMd5"`
+
+	// 标签值MD5值
+	TagValueMd5 *string `json:"TagValueMd5,omitempty" name:"TagValueMd5"`
+}
+
+type TagWithDelete struct {
+
+	// 标签键
+	TagKey *string `json:"TagKey,omitempty" name:"TagKey"`
+
+	// 标签值
+	TagValue *string `json:"TagValue,omitempty" name:"TagValue"`
+
+	// 是否可以删除
+	CanDelete *uint64 `json:"CanDelete,omitempty" name:"CanDelete"`
+}
+
+type UpdateResourceTagValueRequest struct {
+	*tchttp.BaseRequest
+
+	// 资源关联的标签键
+	TagKey *string `json:"TagKey,omitempty" name:"TagKey"`
+
+	// 修改后的标签值
+	TagValue *string `json:"TagValue,omitempty" name:"TagValue"`
+
+	// 资源的六段式描述
+	Resource *string `json:"Resource,omitempty" name:"Resource"`
+}
+
+func (r *UpdateResourceTagValueRequest) ToJsonString() string {
+    b, _ := json.Marshal(r)
+    return string(b)
+}
+
+func (r *UpdateResourceTagValueRequest) FromJsonString(s string) error {
+    return json.Unmarshal([]byte(s), &r)
+}
+
+type UpdateResourceTagValueResponse struct {
+	*tchttp.BaseResponse
+	Response *struct {
+
+		// 唯一请求 ID,每次请求都会返回。定位问题时需要提供该次请求的 RequestId。
+		RequestId *string `json:"RequestId,omitempty" name:"RequestId"`
+	} `json:"Response"`
+}
+
+func (r *UpdateResourceTagValueResponse) ToJsonString() string {
+    b, _ := json.Marshal(r)
+    return string(b)
+}
+
+func (r *UpdateResourceTagValueResponse) FromJsonString(s string) error {
+    return json.Unmarshal([]byte(s), &r)
+}
diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/.bumpversion.cfg b/vendor/github.com/tencentyun/cos-go-sdk-v5/.bumpversion.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..d2bac6df90e4ea50e55274b2a64b079955253edd
--- /dev/null
+++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/.bumpversion.cfg
@@ -0,0 +1,7 @@
+[bumpversion]
+commit = True
+tag = True
+current_version = 0.7.0
+
+[bumpversion:file:cos.go]
+
diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/.gitignore b/vendor/github.com/tencentyun/cos-go-sdk-v5/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..7da68739af02e05bad70485173b6a6551029f0a4
--- /dev/null
+++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/.gitignore
@@ -0,0 +1,29 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
+dist/
+cover.html
+cover.out
+covprofile
+coverage.html
\ No newline at end of file
diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/.travis.yml b/vendor/github.com/tencentyun/cos-go-sdk-v5/.travis.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e940c8c278abef7cabfb7ca914d1c1ff58b8ce1b
--- /dev/null
+++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/.travis.yml
@@ -0,0 +1,38 @@
+language: go
+go:
+- '1.7'
+- '1.8'
+- '1.9'
+- 1.10.x
+- 1.11.x
+- 1.12.x
+- master
+sudo: false
+before_install:
+- go get -u github.com/mattn/goveralls
+- go get -u github.com/stretchr/testify
+install:
+- go get
+- go build
+- go build github.com/mattn/goveralls
+script:
+- if [[ ! -n "$COS_SECRETID" ]]; then exit 0
+  ; fi
+- make test
+- make ci-test
+- go test -coverprofile=cover.out github.com/toranger/cos-go-sdk-v5
+- "${TRAVIS_HOME}/gopath/bin/goveralls -service=travis-ci -coverprofile=cover.out"
+matrix:
+  allow_failures:
+  - go: 1.7
+  - go: master
+env:
+  global:
+  - secure: XXB/cFVnJcAzhOZ2/zplwjhhhireQQGGRbNscPgQ0kpUQCyPZ6oIHvJMafuP4TVTJHEdMiaDxm0HNvgARuopXVaQNmK2UZj6xw40Ud7OT7ZUnw88xkQkXOI5GwG8oz9LqxIXUSItHegKXRLW0e1PoBdjZNv6lxGFAtuOcl9ekAg/q2lGIIQFefz6NK7gCmGYULKe+4J15VFldoYNM0JesxxxArTvtv8+k+U53oUwy9dex6z5oIA1zGIeKLcOD2xXgbjid/Ett3t0B2w3GfJWoM9rGV0eHgveOAUGe5tQkMKvl5LK1hj+93ZmU0MAG7x7t9jYKrFPqU/eDNJRMb4Ro6L7lIXVEKaBUkLx28PnwFQ5D043GBVtQGqYNcldZXIfbyYEHQZlD/BWFOt5YqTpGg+7Wm4NC3Yffqsurzk54juT7FftzVy0A8MFkqO+c5RHrOSUlm01pWXkGLHgZhUP5gEZEuUaoluSQTZksmAUJZ7F8DxwpE4SYBqfN27PZ87rWDNyOqNv1w1trzwx2IfdHHA+vfCZ7UM5e85gxFWUO2tJCUai2q21v3gBrcAgBOb6BwVzbWAorM2zY20f0l21XxOWMakA+r4JJA3s3EmcczcQeeL6pkFIAh+qKdFEPuyQTjH1mGpPzYFNbWtvPXijQo5PqyGrKL8W1t3ovwXMXoE=
+  - secure: bep0PPD/oYW5zY0QpeeC+WgFIya5DNRVmR92MO+e5BdFlSJPhstoG8bRh91EeftzC/Hyd3PUEIglPqTgZPxwysqW/81plsU95wV3qJi9gPi7+ZtYXH4xZTnaqgZsTr7jsKSVoKHSu7XqCtbSytW8YMN9wRWzG19/9hX2Z79Q6yNy5l9856Oyj1E2IXDjdZLPsWDhnZ8Vvk1wAVy2fc2esqKzHAZwm8n9vee2yR8vz7GXUszzpKvn4R43eNzdlFEHCmN0ANmxLJZmnYDpZHHfNf4slts+0S6I7awFXppuXUDaJPBRCia4XoFeSw+01IW1Vi0kAwvGLhxjJCWc4M/4ZU0byXDT11tDFvWa19NmnbYiizWiXNVecn1oNWYJqIKe7TTAMAtHSXAPmLX0rXuXKzwM09W6yrLFufCxyix9IOnenEbe9WwSdBbhmeLF3Wu/uVGkDog/FsXJM75sk956vV9UKh9zF4B9/NR8szJMF7shEs0Fbru5UUWheqg4AadPl3dhAWuj2+6NANa1LpH3JVD3II9dlXeMmMvsSwDvrYUaX/S8tf6JwZG0zCJK0TYp05rjxH+NIzWaMUTY7+HwYqqK3pOW3San0SlZiMq8N7GSnKUZ7WRQXYSB4gXHrg+mWyeVC7XnqiRtCwVi+LtPMu+YUbg7dwVi0vtKjYZYIUY=
+  - secure: Ob28vrOuHMKNKEtChkWbsaVv2SwLhcxXMnvGe4XN+y3mFvdhYnwpt6NdgThF8OCZ0761tvTRmvALfiZnO0uORjTtoHKkVPrnVIxlCcode0NVJZNHGn2fqjemdLKCnSeX7hm+9zeLpCnIvC+Sp3iZ3t2AH4AzgFx6nirWO3HwT5l9rNL9Q1CfwlOpNJJ36r9JTHwQnXmOfOmszUNoZ3rtiFXJ8dCi+BgY0lsiIRSiDkAH7KAPf86REM+ww81AaXG4/RuYx1Vj5zQCtZN7XEOViSXEbqqb8SrIFOccDu5FV12djg+4QS7FSjLVGrdIUcn4oI6pS24Et3oXf8xFx6JLYyGGhgZ2BsyJEx5vLQvkTWnMTrwZVRtCQ+g6lMUQpJhL2rBrmVBUqBFb5IH69O7corQm53n5qLM8IiosAQLfbOtML/1PyEpKCG2aOx1377Fx2yzxXW3ucP1PBqCzli0oCM2T52LfiNvZTzkIU6XJebBnzkZXepzOIFSur86kxgvQFElw9ro2X6XXPKU5S25xVaUSvaN1kmqLSkToJ9S1rmDYXnJR4aH0R2GcLw+EkMHFJJoAjnRHxrB4/1vOJbzmfS+qy6ShRhUMSD8gk4YJ6Y7o9h7oekuWOEn+XGhl29U9T5OApzHfoPEGZwLnpHxAiKJtQtv/TNhBIOFCjigsF7U=
+notifications:
+  email:
+    recipients:
+      - wjielai@tencent.com
+      - fysntian@tencent.com
diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/LICENSE b/vendor/github.com/tencentyun/cos-go-sdk-v5/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..8ff7942e2030d9fa4bb1c8766d184abd9d45a695
--- /dev/null
+++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 mozillazg
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/Makefile b/vendor/github.com/tencentyun/cos-go-sdk-v5/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..e3b7aa31934fd6384bcba57f42ab2581bc4d8420
--- /dev/null
+++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/Makefile
@@ -0,0 +1,22 @@
+help:
+	@echo "test             run test"
+	@echo "lint             run lint"
+	@echo "example          run examples"
+
+.PHONY: test
+test:
+	go test -v -cover -coverprofile cover.out
+	go tool cover -html=cover.out -o cover.html
+
+.PHONY: lint
+lint:
+	gofmt -s -w .
+	goimports -w .
+	golint .
+	go vet
+
+.PHONY: example
+example:
+	cd example && sh test.sh
+ci-test:
+	cd costesting && go test -v 
diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/README.md b/vendor/github.com/tencentyun/cos-go-sdk-v5/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..0316a137ba392fce79d0a3040dadae25926c552d
--- /dev/null
+++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/README.md
@@ -0,0 +1,95 @@
+# cos-go-sdk-v5
+
+腾讯云对象存储服务 COS(Cloud Object Storage) Go SDK(API 版本:V5 版本的 XML API)。
+
+## Install
+
+`go get -u github.com/tencentyun/cos-go-sdk-v5`
+
+
+## Usage
+
+```go
+package main
+
+import (
+	"context"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"net/url"
+	"os"
+	"time"
+	
+	"github.com/tencentyun/cos-go-sdk-v5"
+)
+
+func main() {
+	//将<bucket>和<region>修改为真实的信息
+	//bucket的命名规则为{name}-{appid} ,此处填写的存储桶名称必须为此格式
+	u, _ := url.Parse("https://<bucket>.cos.<region>.myqcloud.com")
+	b := &cos.BaseURL{BucketURL: u}
+	c := cos.NewClient(b, &http.Client{
+		//设置超时时间
+		Timeout: 100 * time.Second,
+		Transport: &cos.AuthorizationTransport{
+			//如实填写账号和密钥,也可以设置为环境变量
+			SecretID:  os.Getenv("COS_SECRETID"),
+			SecretKey: os.Getenv("COS_SECRETKEY"),
+		},
+	})
+
+	name := "test/hello.txt"
+	resp, err := c.Object.Get(context.Background(), name, nil)
+	if err != nil {
+		panic(err)
+	}
+	bs, _ := ioutil.ReadAll(resp.Body)
+	resp.Body.Close()
+	fmt.Printf("%s\n", string(bs))
+}
+```
+
+所有的 API 在 [example](./example/) 目录下都有对应的使用示例。
+
+Service API:
+
+* [x] Get Service(使用示例:[service/get.go](./example/service/get.go))
+
+Bucket API:
+
+* [x] Get Bucket(使用示例:[bucket/get.go](./example/bucket/get.go))
+* [x] Get Bucket ACL(使用示例:[bucket/getACL.go](./example/bucket/getACL.go))
+* [x] Get Bucket CORS(使用示例:[bucket/getCORS.go](./example/bucket/getCORS.go))
+* [x] Get Bucket Location(使用示例:[bucket/getLocation.go](./example/bucket/getLocation.go))
+* [x] Get Buket Lifecycle(使用示例:[bucket/getLifecycle.go](./example/bucket/getLifecycle.go))
+* [x] Get Bucket Tagging(使用示例:[bucket/getTagging.go](./example/bucket/getTagging.go))
+* [x] Put Bucket(使用示例:[bucket/put.go](./example/bucket/put.go))
+* [x] Put Bucket ACL(使用示例:[bucket/putACL.go](./example/bucket/putACL.go))
+* [x] Put Bucket CORS(使用示例:[bucket/putCORS.go](./example/bucket/putCORS.go))
+* [x] Put Bucket Lifecycle(使用示例:[bucket/putLifecycle.go](./example/bucket/putLifecycle.go))
+* [x] Put Bucket Tagging(使用示例:[bucket/putTagging.go](./example/bucket/putTagging.go))
+* [x] Delete Bucket(使用示例:[bucket/delete.go](./example/bucket/delete.go))
+* [x] Delete Bucket CORS(使用示例:[bucket/deleteCORS.go](./example/bucket/deleteCORS.go))
+* [x] Delete Bucket Lifecycle(使用示例:[bucket/deleteLifecycle.go](./example/bucket/deleteLifecycle.go))
+* [x] Delete Bucket Tagging(使用示例:[bucket/deleteTagging.go](./example/bucket/deleteTagging.go))
+* [x] Head Bucket(使用示例:[bucket/head.go](./example/bucket/head.go))
+* [x] List Multipart Uploads(使用示例:[bucket/listMultipartUploads.go](./example/bucket/listMultipartUploads.go))
+
+Object API:
+
+* [x] Get Object(使用示例:[object/get.go](./example/object/get.go))
+* [x] Get Object ACL(使用示例:[object/getACL.go](./example/object/getACL.go))
+* [x] Put Object(使用示例:[object/put.go](./example/object/put.go))
+* [x] Put Object ACL(使用示例:[object/putACL.go](./example/object/putACL.go))
+* [x] Put Object Copy(使用示例:[object/copy.go](./example/object/copy.go))
+* [x] Delete Object(使用示例:[object/delete.go](./example/object/delete.go))
+* [x] Delete Multiple Object(使用示例:[object/deleteMultiple.go](./example/object/deleteMultiple.go))
+* [x] Head Object(使用示例:[object/head.go](./example/object/head.go))
+* [x] Options Object(使用示例:[object/options.go](./example/object/options.go))
+* [x] Initiate Multipart Upload(使用示例:[object/initiateMultipartUpload.go](./example/object/initiateMultipartUpload.go))
+* [x] Upload Part(使用示例:[object/uploadPart.go](./example/object/uploadPart.go))
+* [x] List Parts(使用示例:[object/listParts.go](./example/object/listParts.go))
+* [x] Complete Multipart Upload(使用示例:[object/completeMultipartUpload.go](./example/object/completeMultipartUpload.go))
+* [x] Abort Multipart Upload(使用示例:[object/abortMultipartUpload.go](./example/object/abortMultipartUpload.go))
+* [x] Mutipart Upload(使用示例:[object/MutiUpload.go](./example/object/MutiUpload.go))
diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/auth.go b/vendor/github.com/tencentyun/cos-go-sdk-v5/auth.go
new file mode 100644
index 0000000000000000000000000000000000000000..6b0cc554cf104539b9b5742ce5609ad5e7ba9ddf
--- /dev/null
+++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/auth.go
@@ -0,0 +1,305 @@
+package cos
+
+import (
+	"crypto/hmac"
+	"crypto/sha1"
+	"fmt"
+	"hash"
+	"net/http"
+	"net/url"
+	"sort"
+	"strings"
+	"sync"
+	"time"
+)
+
+const sha1SignAlgorithm = "sha1"
+const privateHeaderPrefix = "x-cos-"
+const defaultAuthExpire = time.Hour
+
+// 需要校验的 Headers 列表
+var needSignHeaders = map[string]bool{
+	"host":                           true,
+	"range":                          true,
+	"x-cos-acl":                      true,
+	"x-cos-grant-read":               true,
+	"x-cos-grant-write":              true,
+	"x-cos-grant-full-control":       true,
+	"response-content-type":          true,
+	"response-content-language":      true,
+	"response-expires":               true,
+	"response-cache-control":         true,
+	"response-content-disposition":   true,
+	"response-content-encoding":      true,
+	"cache-control":                  true,
+	"content-disposition":            true,
+	"content-encoding":               true,
+	"content-type":                   true,
+	"content-length":                 true,
+	"content-md5":                    true,
+	"expect":                         true,
+	"expires":                        true,
+	"x-cos-content-sha1":             true,
+	"x-cos-storage-class":            true,
+	"if-modified-since":              true,
+	"origin":                         true,
+	"access-control-request-method":  true,
+	"access-control-request-headers": true,
+	"x-cos-object-type":              true,
+}
+
+func safeURLEncode(s string) string {
+	s = encodeURIComponent(s)
+	s = strings.Replace(s, "!", "%21", -1)
+	s = strings.Replace(s, "'", "%27", -1)
+	s = strings.Replace(s, "(", "%28", -1)
+	s = strings.Replace(s, ")", "%29", -1)
+	s = strings.Replace(s, "*", "%2A", -1)
+	return s
+}
+
+type valuesSignMap map[string][]string
+
+func (vs valuesSignMap) Add(key, value string) {
+	key = strings.ToLower(key)
+	vs[key] = append(vs[key], value)
+}
+
+func (vs valuesSignMap) Encode() string {
+	var keys []string
+	for k := range vs {
+		keys = append(keys, k)
+	}
+	sort.Strings(keys)
+
+	var pairs []string
+	for _, k := range keys {
+		items := vs[k]
+		sort.Strings(items)
+		for _, val := range items {
+			pairs = append(
+				pairs,
+				fmt.Sprintf("%s=%s", safeURLEncode(k), safeURLEncode(val)))
+		}
+	}
+	return strings.Join(pairs, "&")
+}
+
+// AuthTime 用于生成签名所需的 q-sign-time 和 q-key-time 相关参数
+type AuthTime struct {
+	SignStartTime time.Time
+	SignEndTime   time.Time
+	KeyStartTime  time.Time
+	KeyEndTime    time.Time
+}
+
+// NewAuthTime 生成 AuthTime 的便捷函数
+//
+//   expire: 从现在开始多久过期.
+func NewAuthTime(expire time.Duration) *AuthTime {
+	signStartTime := time.Now()
+	keyStartTime := signStartTime
+	signEndTime := signStartTime.Add(expire)
+	keyEndTime := signEndTime
+	return &AuthTime{
+		SignStartTime: signStartTime,
+		SignEndTime:   signEndTime,
+		KeyStartTime:  keyStartTime,
+		KeyEndTime:    keyEndTime,
+	}
+}
+
+// signString return q-sign-time string
+func (a *AuthTime) signString() string {
+	return fmt.Sprintf("%d;%d", a.SignStartTime.Unix(), a.SignEndTime.Unix())
+}
+
+// keyString return q-key-time string
+func (a *AuthTime) keyString() string {
+	return fmt.Sprintf("%d;%d", a.KeyStartTime.Unix(), a.KeyEndTime.Unix())
+}
+
+// newAuthorization 通过一系列步骤生成最终需要的 Authorization 字符串
+func newAuthorization(secretID, secretKey string, req *http.Request, authTime *AuthTime) string {
+	signTime := authTime.signString()
+	keyTime := authTime.keyString()
+	signKey := calSignKey(secretKey, keyTime)
+
+	formatHeaders := *new(string)
+	signedHeaderList := *new([]string)
+	formatHeaders, signedHeaderList = genFormatHeaders(req.Header)
+	formatParameters, signedParameterList := genFormatParameters(req.URL.Query())
+	formatString := genFormatString(req.Method, *req.URL, formatParameters, formatHeaders)
+
+	stringToSign := calStringToSign(sha1SignAlgorithm, keyTime, formatString)
+	signature := calSignature(signKey, stringToSign)
+
+	return genAuthorization(
+		secretID, signTime, keyTime, signature, signedHeaderList,
+		signedParameterList,
+	)
+}
+
+// AddAuthorizationHeader 给 req 增加签名信息
+func AddAuthorizationHeader(secretID, secretKey string, sessionToken string, req *http.Request, authTime *AuthTime) {
+	if secretID == "" {
+		return
+	}
+
+	auth := newAuthorization(secretID, secretKey, req,
+		authTime,
+	)
+	if len(sessionToken) > 0 {
+		req.Header.Set("x-cos-security-token", sessionToken)
+	}
+	req.Header.Set("Authorization", auth)
+}
+
+// calSignKey 计算 SignKey
+func calSignKey(secretKey, keyTime string) string {
+	digest := calHMACDigest(secretKey, keyTime, sha1SignAlgorithm)
+	return fmt.Sprintf("%x", digest)
+}
+
+// calStringToSign 计算 StringToSign
+func calStringToSign(signAlgorithm, signTime, formatString string) string {
+	h := sha1.New()
+	h.Write([]byte(formatString))
+	return fmt.Sprintf("%s\n%s\n%x\n", signAlgorithm, signTime, h.Sum(nil))
+}
+
+// calSignature 计算 Signature
+func calSignature(signKey, stringToSign string) string {
+	digest := calHMACDigest(signKey, stringToSign, sha1SignAlgorithm)
+	return fmt.Sprintf("%x", digest)
+}
+
+// genAuthorization 生成 Authorization
+func genAuthorization(secretID, signTime, keyTime, signature string, signedHeaderList, signedParameterList []string) string {
+	return strings.Join([]string{
+		"q-sign-algorithm=" + sha1SignAlgorithm,
+		"q-ak=" + secretID,
+		"q-sign-time=" + signTime,
+		"q-key-time=" + keyTime,
+		"q-header-list=" + strings.Join(signedHeaderList, ";"),
+		"q-url-param-list=" + strings.Join(signedParameterList, ";"),
+		"q-signature=" + signature,
+	}, "&")
+}
+
+// genFormatString 生成 FormatString
+func genFormatString(method string, uri url.URL, formatParameters, formatHeaders string) string {
+	formatMethod := strings.ToLower(method)
+	formatURI := uri.Path
+
+	return fmt.Sprintf("%s\n%s\n%s\n%s\n", formatMethod, formatURI,
+		formatParameters, formatHeaders,
+	)
+}
+
+// genFormatParameters 生成 FormatParameters 和 SignedParameterList
+// instead of the url.Values{}
+func genFormatParameters(parameters url.Values) (formatParameters string, signedParameterList []string) {
+	ps := valuesSignMap{}
+	for key, values := range parameters {
+		key = strings.ToLower(key)
+		for _, value := range values {
+			ps.Add(key, value)
+			signedParameterList = append(signedParameterList, key)
+		}
+	}
+	//formatParameters = strings.ToLower(ps.Encode())
+	formatParameters = ps.Encode()
+	sort.Strings(signedParameterList)
+	return
+}
+
+// genFormatHeaders 生成 FormatHeaders 和 SignedHeaderList
+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) {
+				hs.Add(key, value)
+				signedHeaderList = append(signedHeaderList, key)
+			}
+		}
+	}
+	formatHeaders = hs.Encode()
+	sort.Strings(signedHeaderList)
+	return
+}
+
+// HMAC 签名
+func calHMACDigest(key, msg, signMethod string) []byte {
+	var hashFunc func() hash.Hash
+	switch signMethod {
+	case "sha1":
+		hashFunc = sha1.New
+	default:
+		hashFunc = sha1.New
+	}
+	h := hmac.New(hashFunc, []byte(key))
+	h.Write([]byte(msg))
+	return h.Sum(nil)
+}
+
+func isSignHeader(key string) bool {
+	for k, v := range needSignHeaders {
+		if key == k && v {
+			return true
+		}
+	}
+	return strings.HasPrefix(key, privateHeaderPrefix)
+}
+
+// AuthorizationTransport 给请求增加 Authorization header
+type AuthorizationTransport struct {
+	SecretID     string
+	SecretKey    string
+	SessionToken string
+	rwLocker     sync.RWMutex
+	// 签名多久过期
+	Expire    time.Duration
+	Transport http.RoundTripper
+}
+
+// SetCredential update the SecretID(ak), SercretKey(sk), sessiontoken
+func (t *AuthorizationTransport) SetCredential(ak, sk, token string) {
+	t.rwLocker.Lock()
+	defer t.rwLocker.Unlock()
+	t.SecretID = ak
+	t.SecretKey = sk
+	t.SessionToken = token
+}
+
+// GetCredential get the ak, sk, token
+func (t *AuthorizationTransport) GetCredential() (string, string, string) {
+	t.rwLocker.RLock()
+	defer t.rwLocker.RUnlock()
+	return t.SecretID, t.SecretKey, t.SessionToken
+}
+
+// RoundTrip implements the RoundTripper interface.
+func (t *AuthorizationTransport) RoundTrip(req *http.Request) (*http.Response, error) {
+	req = cloneRequest(req) // per RoundTrip contract
+	if t.Expire == time.Duration(0) {
+		t.Expire = defaultAuthExpire
+	}
+
+	ak, sk, token := t.GetCredential()
+	// 增加 Authorization header
+	authTime := NewAuthTime(t.Expire)
+	AddAuthorizationHeader(ak, sk, token, req, authTime)
+
+	resp, err := t.transport().RoundTrip(req)
+	return resp, err
+}
+
+func (t *AuthorizationTransport) 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
new file mode 100644
index 0000000000000000000000000000000000000000..2e3f92c2f6ea6a0b12fea6b847397eb8ad08f98f
--- /dev/null
+++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket.go
@@ -0,0 +1,104 @@
+package cos
+
+import (
+	"context"
+	"encoding/xml"
+	"net/http"
+)
+
+// BucketService 相关 API
+type BucketService service
+
+// BucketGetResult is the result of GetBucket
+type BucketGetResult struct {
+	XMLName        xml.Name `xml:"ListBucketResult"`
+	Name           string
+	Prefix         string `xml:"Prefix,omitempty"`
+	Marker         string `xml:"Marker,omitempty"`
+	NextMarker     string `xml:"NextMarker,omitempty"`
+	Delimiter      string `xml:"Delimiter,omitempty"`
+	MaxKeys        int
+	IsTruncated    bool
+	Contents       []Object `xml:"Contents,omitempty"`
+	CommonPrefixes []string `xml:"CommonPrefixes>Prefix,omitempty"`
+	EncodingType   string   `xml:"Encoding-Type,omitempty"`
+}
+
+// BucketGetOptions is the option of GetBucket
+type BucketGetOptions struct {
+	Prefix       string `url:"prefix,omitempty"`
+	Delimiter    string `url:"delimiter,omitempty"`
+	EncodingType string `url:"encoding-type,omitempty"`
+	Marker       string `url:"marker,omitempty"`
+	MaxKeys      int    `url:"max-keys,omitempty"`
+}
+
+// Get Bucket请求等同于 List Object请求,可以列出该Bucket下部分或者所有Object,发起该请求需要拥有Read权限。
+//
+// https://www.qcloud.com/document/product/436/7734
+func (s *BucketService) Get(ctx context.Context, opt *BucketGetOptions) (*BucketGetResult, *Response, error) {
+	var res BucketGetResult
+	sendOpt := sendOptions{
+		baseURL:  s.client.BaseURL.BucketURL,
+		uri:      "/",
+		method:   http.MethodGet,
+		optQuery: opt,
+		result:   &res,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return &res, resp, err
+}
+
+// BucketPutOptions is same to the ACLHeaderOptions
+type BucketPutOptions ACLHeaderOptions
+
+// Put Bucket请求可以在指定账号下创建一个Bucket。
+//
+// https://www.qcloud.com/document/product/436/7738
+func (s *BucketService) Put(ctx context.Context, opt *BucketPutOptions) (*Response, error) {
+	sendOpt := sendOptions{
+		baseURL:   s.client.BaseURL.BucketURL,
+		uri:       "/",
+		method:    http.MethodPut,
+		optHeader: opt,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return resp, err
+}
+
+// Delete Bucket请求可以在指定账号下删除Bucket,删除之前要求Bucket为空。
+//
+// https://www.qcloud.com/document/product/436/7732
+func (s *BucketService) Delete(ctx context.Context) (*Response, error) {
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.BucketURL,
+		uri:     "/",
+		method:  http.MethodDelete,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return resp, err
+}
+
+// Head Bucket请求可以确认是否存在该Bucket,是否有权限访问,Head的权限与Read一致。
+//
+//   当其存在时,返回 HTTP 状态码200;
+//   当无权限时,返回 HTTP 状态码403;
+//   当不存在时,返回 HTTP 状态码404。
+//
+// https://www.qcloud.com/document/product/436/7735
+func (s *BucketService) Head(ctx context.Context) (*Response, error) {
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.BucketURL,
+		uri:     "/",
+		method:  http.MethodHead,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return resp, err
+}
+
+// Bucket is the meta info of Bucket
+type Bucket struct {
+	Name         string
+	Region       string `xml:"Location,omitempty"`
+	CreationDate string `xml:",omitempty"`
+}
diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket_acl.go b/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket_acl.go
new file mode 100644
index 0000000000000000000000000000000000000000..285b9064b4992262fc3a4e7fb58806284155fa65
--- /dev/null
+++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket_acl.go
@@ -0,0 +1,62 @@
+package cos
+
+import (
+	"context"
+	"net/http"
+)
+
+// BucketGetACLResult is same to the ACLXml
+type BucketGetACLResult ACLXml
+
+// GetACL 使用API读取Bucket的ACL表,只有所有者有权操作。
+//
+// https://www.qcloud.com/document/product/436/7733
+func (s *BucketService) GetACL(ctx context.Context) (*BucketGetACLResult, *Response, error) {
+	var res BucketGetACLResult
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.BucketURL,
+		uri:     "/?acl",
+		method:  http.MethodGet,
+		result:  &res,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return &res, resp, err
+}
+
+// BucketPutACLOptions is the option of PutBucketACL
+type BucketPutACLOptions struct {
+	Header *ACLHeaderOptions `url:"-" xml:"-"`
+	Body   *ACLXml           `url:"-" header:"-"`
+}
+
+// PutACL 使用API写入Bucket的ACL表,您可以通过Header:"x-cos-acl","x-cos-grant-read",
+// "x-cos-grant-write","x-cos-grant-full-control"传入ACL信息,也可以通过body以XML格式传入ACL信息,
+//
+// 但是只能选择Header和Body其中一种,否则返回冲突。
+//
+// Put Bucket ACL是一个覆盖操作,传入新的ACL将覆盖原有ACL。只有所有者有权操作。
+//
+//   "x-cos-acl":枚举值为public-read,private;public-read意味这个Bucket有公有读私有写的权限,
+//   private意味这个Bucket有私有读写的权限。
+//
+//   "x-cos-grant-read":意味被赋予权限的用户拥有该Bucket的读权限
+//   "x-cos-grant-write":意味被赋予权限的用户拥有该Bucket的写权限
+//   "x-cos-grant-full-control":意味被赋予权限的用户拥有该Bucket的读写权限
+//
+// https://www.qcloud.com/document/product/436/7737
+func (s *BucketService) PutACL(ctx context.Context, opt *BucketPutACLOptions) (*Response, error) {
+	header := opt.Header
+	body := opt.Body
+	if body != nil {
+		header = nil
+	}
+	sendOpt := sendOptions{
+		baseURL:   s.client.BaseURL.BucketURL,
+		uri:       "/?acl",
+		method:    http.MethodPut,
+		body:      body,
+		optHeader: header,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return resp, err
+}
diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket_cors.go b/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket_cors.go
new file mode 100644
index 0000000000000000000000000000000000000000..1d688c9b20d4cc321bc4820a21074cccc3ba7a2f
--- /dev/null
+++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket_cors.go
@@ -0,0 +1,71 @@
+package cos
+
+import (
+	"context"
+	"encoding/xml"
+	"net/http"
+)
+
+// BucketCORSRule is the rule of BucketCORS
+type BucketCORSRule struct {
+	ID             string   `xml:"ID,omitempty"`
+	AllowedMethods []string `xml:"AllowedMethod"`
+	AllowedOrigins []string `xml:"AllowedOrigin"`
+	AllowedHeaders []string `xml:"AllowedHeader,omitempty"`
+	MaxAgeSeconds  int      `xml:"MaxAgeSeconds,omitempty"`
+	ExposeHeaders  []string `xml:"ExposeHeader,omitempty"`
+}
+
+// BucketGetCORSResult is the result of GetBucketCORS
+type BucketGetCORSResult struct {
+	XMLName xml.Name         `xml:"CORSConfiguration"`
+	Rules   []BucketCORSRule `xml:"CORSRule,omitempty"`
+}
+
+// GetCORS 实现 Bucket 跨域访问配置读取。
+//
+// https://www.qcloud.com/document/product/436/8274
+func (s *BucketService) GetCORS(ctx context.Context) (*BucketGetCORSResult, *Response, error) {
+	var res BucketGetCORSResult
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.BucketURL,
+		uri:     "/?cors",
+		method:  http.MethodGet,
+		result:  &res,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return &res, resp, err
+}
+
+// BucketPutCORSOptions is the option of PutBucketCORS 
+type BucketPutCORSOptions struct {
+	XMLName xml.Name         `xml:"CORSConfiguration"`
+	Rules   []BucketCORSRule `xml:"CORSRule,omitempty"`
+}
+
+// PutCORS 实现 Bucket 跨域访问设置,您可以通过传入XML格式的配置文件实现配置,文件大小限制为64 KB。
+//
+// https://www.qcloud.com/document/product/436/8279
+func (s *BucketService) PutCORS(ctx context.Context, opt *BucketPutCORSOptions) (*Response, error) {
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.BucketURL,
+		uri:     "/?cors",
+		method:  http.MethodPut,
+		body:    opt,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return resp, err
+}
+
+// DeleteCORS 实现 Bucket 跨域访问配置删除。
+//
+// https://www.qcloud.com/document/product/436/8283
+func (s *BucketService) DeleteCORS(ctx context.Context) (*Response, error) {
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.BucketURL,
+		uri:     "/?cors",
+		method:  http.MethodDelete,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return resp, err
+}
diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket_inventory.go b/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket_inventory.go
new file mode 100644
index 0000000000000000000000000000000000000000..17ed781e98d7db5ce5a2a5d846217c939e3eb722
--- /dev/null
+++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket_inventory.go
@@ -0,0 +1,134 @@
+package cos
+
+import (
+	"context"
+	"encoding/xml"
+	"fmt"
+	"net/http"
+)
+
+// Notice bucket_inventory only for test. can not use
+
+// BucketGetInventoryResult same struct to options
+type BucketGetInventoryResult BucketPutInventoryOptions
+
+// BucketListInventoryConfiguartion same struct to options
+type BucketListInventoryConfiguartion BucketPutInventoryOptions
+
+// BucketInventoryFilter ...
+type BucketInventoryFilter struct {
+	Prefix string `xml:"Prefix,omitempty"`
+}
+
+// BucketInventoryOptionalFields ...
+type BucketInventoryOptionalFields struct {
+	XMLName               xml.Name `xml:"OptionalFields,omitempty"`
+	BucketInventoryFields []string `xml:"Field,omitempty"`
+}
+
+// BucketInventorySchedule ...
+type BucketInventorySchedule struct {
+	Frequency string `xml:"Frequency"`
+}
+
+// BucketInventoryEncryption ...
+type BucketInventoryEncryption struct {
+	XMLName xml.Name `xml:"Encryption"`
+	SSECOS  string   `xml:"SSE-COS,omitempty"`
+}
+
+// BucketInventoryDestinationContent ...
+type BucketInventoryDestinationContent struct {
+	Bucket     string                     `xml:"Bucket"`
+	AccountId  string                     `xml:"AccountId,omitempty"`
+	Prefix     string                     `xml:"Prefix,omitempty"`
+	Format     string                     `xml:"Format"`
+	Encryption *BucketInventoryEncryption `xml:"Encryption,omitempty"`
+}
+
+// BucketInventoryDestination ...
+type BucketInventoryDestination struct {
+	XMLName           xml.Name                           `xml:"Destination"`
+	BucketDestination *BucketInventoryDestinationContent `xml:"COSBucketDestination"`
+}
+
+// BucketPutInventoryOptions ...
+type BucketPutInventoryOptions struct {
+	XMLName                xml.Name                       `xml:"InventoryConfiguration"`
+	ID                     string                         `xml:"Id"`
+	IsEnabled              string                         `xml:"IsEnabled"`
+	IncludedObjectVersions string                         `xml:"IncludedObjectVersions"`
+	Filter                 *BucketInventoryFilter         `xml:"Filter,omitempty"`
+	OptionalFields         *BucketInventoryOptionalFields `xml:"OptionalFields,omitempty"`
+	Schedule               *BucketInventorySchedule       `xml:"Schedule"`
+	Destination            *BucketInventoryDestination    `xml:"Destination"`
+}
+
+// ListBucketInventoryConfigResult result of ListBucketInventoryConfiguration
+type ListBucketInventoryConfigResult struct {
+	XMLName                 xml.Name                           `xml:"ListInventoryConfigurationResult"`
+	InventoryConfigurations []BucketListInventoryConfiguartion `xml:"InventoryConfiguration,omitempty"`
+	IsTruncated             bool                               `xml:"IsTruncated,omitempty"`
+	ContinuationToken       string                             `xml:"ContinuationToken,omitempty"`
+	NextContinuationToken   string                             `xml:"NextContinuationToken,omitempty"`
+}
+
+// PutBucketInventory https://cloud.tencent.com/document/product/436/33707
+func (s *BucketService) PutBucketInventoryTest(ctx context.Context, id string, opt *BucketPutInventoryOptions) (*Response, error) {
+	u := fmt.Sprintf("/?inventory&id=%s", id)
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.BucketURL,
+		uri:     u,
+		method:  http.MethodPut,
+		body:    opt,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return resp, err
+
+}
+
+// GetBucketInventory https://cloud.tencent.com/document/product/436/33705
+func (s *BucketService) GetBucketInventoryTest(ctx context.Context, id string) (*BucketGetInventoryResult, *Response, error) {
+	u := fmt.Sprintf("/?inventory&id=%s", id)
+	var res BucketGetInventoryResult
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.BucketURL,
+		uri:     u,
+		method:  http.MethodGet,
+		result:  &res,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return &res, resp, err
+}
+
+// DeleteBucketInventory https://cloud.tencent.com/document/product/436/33704
+func (s *BucketService) DeleteBucketInventoryTest(ctx context.Context, id string) (*Response, error) {
+	u := fmt.Sprintf("/?inventory&id=%s", id)
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.BucketURL,
+		uri:     u,
+		method:  http.MethodDelete,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return resp, err
+}
+
+// ListBucketInventoryConfigurations https://cloud.tencent.com/document/product/436/33706
+func (s *BucketService) ListBucketInventoryConfigurationsTest(ctx context.Context, token string) (*ListBucketInventoryConfigResult, *Response, error) {
+	var res ListBucketInventoryConfigResult
+	var u string
+	if token == "" {
+		u = "/?inventory"
+	} else {
+		u = fmt.Sprintf("/?inventory&continuation-token=%s", encodeURIComponent(token))
+	}
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.BucketURL,
+		uri:     u,
+		method:  http.MethodGet,
+		result:  &res,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return &res, resp, err
+
+}
diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket_lifecycle.go b/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket_lifecycle.go
new file mode 100644
index 0000000000000000000000000000000000000000..c9ca97f4822367e2a07fd73913babd2cb678b96c
--- /dev/null
+++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket_lifecycle.go
@@ -0,0 +1,92 @@
+package cos
+
+import (
+	"context"
+	"encoding/xml"
+	"net/http"
+)
+
+// BucketLifecycleFilter is the param of BucketLifecycleRule
+type BucketLifecycleFilter struct {
+	Prefix string `xml:"Prefix,omitempty"`
+}
+
+// BucketLifecycleExpiration is the param of BucketLifecycleRule
+type BucketLifecycleExpiration struct {
+	Date string `xml:"Date,omitempty"`
+	Days int    `xml:"Days,omitempty"`
+}
+
+// BucketLifecycleTransition is the param of BucketLifecycleRule
+type BucketLifecycleTransition struct {
+	Date         string `xml:"Date,omitempty"`
+	Days         int    `xml:"Days,omitempty"`
+	StorageClass string
+}
+
+// BucketLifecycleAbortIncompleteMultipartUpload is the param of BucketLifecycleRule
+type BucketLifecycleAbortIncompleteMultipartUpload struct {
+	DaysAfterInitiation string `xml:"DaysAfterInititation,omitempty"`
+}
+
+// BucketLifecycleRule is the rule of BucketLifecycle
+type BucketLifecycleRule struct {
+	ID                             string `xml:"ID,omitempty"`
+	Status                         string
+	Filter                         *BucketLifecycleFilter                         `xml:"Filter,omitempty"`
+	Transition                     *BucketLifecycleTransition                     `xml:"Transition,omitempty"`
+	Expiration                     *BucketLifecycleExpiration                     `xml:"Expiration,omitempty"`
+	AbortIncompleteMultipartUpload *BucketLifecycleAbortIncompleteMultipartUpload `xml:"AbortIncompleteMultipartUpload,omitempty"`
+}
+
+// BucketGetLifecycleResult is the result of BucketGetLifecycle
+type BucketGetLifecycleResult struct {
+	XMLName xml.Name              `xml:"LifecycleConfiguration"`
+	Rules   []BucketLifecycleRule `xml:"Rule,omitempty"`
+}
+
+// GetLifecycle 请求实现读取生命周期管理的配置。当配置不存在时,返回404 Not Found。
+// https://www.qcloud.com/document/product/436/8278
+func (s *BucketService) GetLifecycle(ctx context.Context) (*BucketGetLifecycleResult, *Response, error) {
+	var res BucketGetLifecycleResult
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.BucketURL,
+		uri:     "/?lifecycle",
+		method:  http.MethodGet,
+		result:  &res,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return &res, resp, err
+}
+
+// BucketPutLifecycleOptions is the option of PutBucketLifecycle
+type BucketPutLifecycleOptions struct {
+	XMLName xml.Name              `xml:"LifecycleConfiguration"`
+	Rules   []BucketLifecycleRule `xml:"Rule,omitempty"`
+}
+
+// PutLifecycle 请求实现设置生命周期管理的功能。您可以通过该请求实现数据的生命周期管理配置和定期删除。
+// 此请求为覆盖操作,上传新的配置文件将覆盖之前的配置文件。生命周期管理对文件和文件夹同时生效。
+// https://www.qcloud.com/document/product/436/8280
+func (s *BucketService) PutLifecycle(ctx context.Context, opt *BucketPutLifecycleOptions) (*Response, error) {
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.BucketURL,
+		uri:     "/?lifecycle",
+		method:  http.MethodPut,
+		body:    opt,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return resp, err
+}
+
+// DeleteLifecycle 请求实现删除生命周期管理。
+// https://www.qcloud.com/document/product/436/8284
+func (s *BucketService) DeleteLifecycle(ctx context.Context) (*Response, error) {
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.BucketURL,
+		uri:     "/?lifecycle",
+		method:  http.MethodDelete,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return resp, err
+}
diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket_location.go b/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket_location.go
new file mode 100644
index 0000000000000000000000000000000000000000..dd4b5a55d4eef439f8334ae1e0fa690e3ce2c9e8
--- /dev/null
+++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket_location.go
@@ -0,0 +1,28 @@
+package cos
+
+import (
+	"context"
+	"encoding/xml"
+	"net/http"
+)
+
+// BucketGetLocationResult is the result of BucketGetLocation
+type BucketGetLocationResult struct {
+	XMLName  xml.Name `xml:"LocationConstraint"`
+	Location string   `xml:",chardata"`
+}
+
+// GetLocation 接口获取Bucket所在地域信息,只有Bucket所有者有权限读取信息。
+//
+// https://www.qcloud.com/document/product/436/8275
+func (s *BucketService) GetLocation(ctx context.Context) (*BucketGetLocationResult, *Response, error) {
+	var res BucketGetLocationResult
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.BucketURL,
+		uri:     "/?location",
+		method:  http.MethodGet,
+		result:  &res,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return &res, resp, err
+}
diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket_logging.go b/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket_logging.go
new file mode 100644
index 0000000000000000000000000000000000000000..d4ea51300a5a70fef7de19e7c6b9f1a98182e376
--- /dev/null
+++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket_logging.go
@@ -0,0 +1,53 @@
+package cos
+
+import (
+	"context"
+	"encoding/xml"
+	"net/http"
+)
+
+// Notice bucket logging function is testing, can not use.
+
+// BucketLoggingEnabled main struct of logging
+type BucketLoggingEnabled struct {
+	TargetBucket string `xml:"TargetBucket"`
+	TargetPrefix string `xml:"TargetPrefix"`
+}
+
+// BucketPutLoggingOptions is the options of PutBucketLogging
+type BucketPutLoggingOptions struct {
+	XMLName        xml.Name              `xml:"BucketLoggingStatus"`
+	LoggingEnabled *BucketLoggingEnabled `xml:"LoggingEnabled"`
+}
+
+// BucketGetLoggingResult is the result of GetBucketLogging
+type BucketGetLoggingResult struct {
+	XMLName        xml.Name              `xml:"BucketLoggingStatus"`
+	LoggingEnabled *BucketLoggingEnabled `xml:"LoggingEnabled"`
+}
+
+// PutBucketLogging https://cloud.tencent.com/document/product/436/17054
+func (s *BucketService) PutBucketLoggingTest(ctx context.Context, opt *BucketPutLoggingOptions) (*Response, error) {
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.BucketURL,
+		uri:     "/?logging",
+		method:  http.MethodPut,
+		body:    opt,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return resp, err
+}
+
+// GetBucketLogging https://cloud.tencent.com/document/product/436/17053
+func (s *BucketService) GetBucketLoggingTest(ctx context.Context) (*BucketGetLoggingResult, *Response, error) {
+	var res BucketGetLoggingResult
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.BucketURL,
+		uri:     "/?logging",
+		method:  http.MethodGet,
+		result:  &res,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return &res, resp, err
+
+}
diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket_part.go b/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket_part.go
new file mode 100644
index 0000000000000000000000000000000000000000..e77b4207a29cb400734a610a10d4381dc563b23c
--- /dev/null
+++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket_part.go
@@ -0,0 +1,57 @@
+package cos
+
+import (
+	"context"
+	"encoding/xml"
+	"net/http"
+)
+
+// ListMultipartUploadsResult is the result of ListMultipartUploads
+type ListMultipartUploadsResult struct {
+	XMLName            xml.Name `xml:"ListMultipartUploadsResult"`
+	Bucket             string   `xml:"Bucket"`
+	EncodingType       string   `xml:"Encoding-Type"`
+	KeyMarker          string
+	UploadIDMarker     string `xml:"UploadIdMarker"`
+	NextKeyMarker      string
+	NextUploadIDMarker string `xml:"NextUploadIdMarker"`
+	MaxUploads         int
+	IsTruncated        bool
+	Uploads            []struct {
+		Key          string
+		UploadID     string `xml:"UploadId"`
+		StorageClass string
+		Initiator    *Initiator
+		Owner        *Owner
+		Initiated    string
+	} `xml:"Upload,omitempty"`
+	Prefix         string
+	Delimiter      string   `xml:"delimiter,omitempty"`
+	CommonPrefixes []string `xml:"CommonPrefixs>Prefix,omitempty"`
+}
+
+// ListMultipartUploadsOptions is the option of ListMultipartUploads
+type ListMultipartUploadsOptions struct {
+	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"`
+}
+
+// ListMultipartUploads 用来查询正在进行中的分块上传。单次最多列出1000个正在进行中的分块上传。
+//
+// https://www.qcloud.com/document/product/436/7736
+func (s *BucketService) ListMultipartUploads(ctx context.Context, opt *ListMultipartUploadsOptions) (*ListMultipartUploadsResult, *Response, error) {
+	var res ListMultipartUploadsResult
+	sendOpt := sendOptions{
+		baseURL:  s.client.BaseURL.BucketURL,
+		uri:      "/?uploads",
+		method:   http.MethodGet,
+		result:   &res,
+		optQuery: opt,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return &res, resp, err
+}
diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket_replication.go b/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket_replication.go
new file mode 100644
index 0000000000000000000000000000000000000000..d0a1a9af919998a8c399133b8c3dd9c1b1ee3abc
--- /dev/null
+++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket_replication.go
@@ -0,0 +1,73 @@
+package cos
+
+import (
+	"context"
+	"encoding/xml"
+	"net/http"
+)
+
+// ReplicationDestination is the sub struct of BucketReplicationRule
+type ReplicationDestination struct {
+	Bucket       string `xml:"Bucket"`
+	StorageClass string `xml:"StorageClass,omitempty"`
+}
+
+// BucketReplicationRule is the main param of replication
+type BucketReplicationRule struct {
+	ID          string                  `xml:"ID,omitempty"`
+	Status      string                  `xml:"Status"`
+	Prefix      string                  `xml:"Prefix"`
+	Destination *ReplicationDestination `xml:"Destination"`
+}
+
+// PutBucketReplicationOptions is the options of PutBucketReplication
+type PutBucketReplicationOptions struct {
+	XMLName xml.Name                `xml:"ReplicationConfiguration"`
+	Role    string                  `xml:"Role"`
+	Rule    []BucketReplicationRule `xml:"Rule"`
+}
+
+//  GetBucketReplicationResult is the result of GetBucketReplication
+type GetBucketReplicationResult struct {
+	XMLName xml.Name                `xml:"ReplicationConfiguration"`
+	Role    string                  `xml:"Role"`
+	Rule    []BucketReplicationRule `xml:"Rule"`
+}
+
+// PutBucketReplication https://cloud.tencent.com/document/product/436/19223
+func (s *BucketService) PutBucketReplication(ctx context.Context, opt *PutBucketReplicationOptions) (*Response, error) {
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.BucketURL,
+		uri:     "/?replication",
+		method:  http.MethodPut,
+		body:    opt,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return resp, err
+
+}
+
+// GetBucketReplication https://cloud.tencent.com/document/product/436/19222
+func (s *BucketService) GetBucketReplication(ctx context.Context) (*GetBucketReplicationResult, *Response, error) {
+	var res GetBucketReplicationResult
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.BucketURL,
+		uri:     "/?replication",
+		method:  http.MethodGet,
+		result:  &res,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return &res, resp, err
+
+}
+
+// DeleteBucketReplication https://cloud.tencent.com/document/product/436/19221
+func (s *BucketService) DeleteBucketReplication(ctx context.Context) (*Response, error) {
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.BucketURL,
+		uri:     "/?replication",
+		method:  http.MethodDelete,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return resp, err
+}
diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket_tagging.go b/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket_tagging.go
new file mode 100644
index 0000000000000000000000000000000000000000..1d8873175e77a9e27e3808cf39aef9a7c40ca69c
--- /dev/null
+++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket_tagging.go
@@ -0,0 +1,69 @@
+package cos
+
+import (
+	"context"
+	"encoding/xml"
+	"net/http"
+)
+
+// BucketTaggingTag is the tag of BucketTagging
+type BucketTaggingTag struct {
+	Key   string
+	Value string
+}
+
+// BucketGetTaggingResult is the result of BucketGetTagging
+type BucketGetTaggingResult struct {
+	XMLName xml.Name           `xml:"Tagging"`
+	TagSet  []BucketTaggingTag `xml:"TagSet>Tag,omitempty"`
+}
+
+// GetTagging 接口实现获取指定Bucket的标签。
+//
+// https://www.qcloud.com/document/product/436/8277
+func (s *BucketService) GetTagging(ctx context.Context) (*BucketGetTaggingResult, *Response, error) {
+	var res BucketGetTaggingResult
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.BucketURL,
+		uri:     "/?tagging",
+		method:  http.MethodGet,
+		result:  &res,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return &res, resp, err
+}
+
+// BucketPutTaggingOptions is the option of BucketPutTagging
+type BucketPutTaggingOptions struct {
+	XMLName xml.Name           `xml:"Tagging"`
+	TagSet  []BucketTaggingTag `xml:"TagSet>Tag,omitempty"`
+}
+
+// PutTagging 接口实现给用指定Bucket打标签。用来组织和管理相关Bucket。
+//
+// 当该请求设置相同Key名称,不同Value时,会返回400。请求成功,则返回204。
+//
+// https://www.qcloud.com/document/product/436/8281
+func (s *BucketService) PutTagging(ctx context.Context, opt *BucketPutTaggingOptions) (*Response, error) {
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.BucketURL,
+		uri:     "/?tagging",
+		method:  http.MethodPut,
+		body:    opt,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return resp, err
+}
+
+// DeleteTagging 接口实现删除指定Bucket的标签。
+//
+// https://www.qcloud.com/document/product/436/8286
+func (s *BucketService) DeleteTagging(ctx context.Context) (*Response, error) {
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.BucketURL,
+		uri:     "/?tagging",
+		method:  http.MethodDelete,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return resp, err
+}
diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket_version.go b/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket_version.go
new file mode 100644
index 0000000000000000000000000000000000000000..b74527bd885a64da00d5323712e5a1c6b51ce6ef
--- /dev/null
+++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/bucket_version.go
@@ -0,0 +1,45 @@
+package cos
+
+import (
+	"context"
+	"encoding/xml"
+	"net/http"
+)
+
+// BucketPutVersionOptions is the options of PutBucketVersioning
+type BucketPutVersionOptions struct {
+	XMLName xml.Name `xml:"VersioningConfiguration"`
+	Status  string   `xml:"Status"`
+}
+
+// BucketGetVersionResult is the result of GetBucketVersioning
+type BucketGetVersionResult struct {
+	XMLName xml.Name `xml:"VersioningConfiguration"`
+	Status  string   `xml:"Status"`
+}
+
+// PutVersion https://cloud.tencent.com/document/product/436/19889
+// Status has Suspended\Enabled
+func (s *BucketService) PutVersioning(ctx context.Context, opt *BucketPutVersionOptions) (*Response, error) {
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.BucketURL,
+		uri:     "/?versioning",
+		method:  http.MethodPut,
+		body:    opt,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return resp, err
+}
+
+// GetVersion https://cloud.tencent.com/document/product/436/19888
+func (s *BucketService) GetVersioning(ctx context.Context) (*BucketGetVersionResult, *Response, error) {
+	var res BucketGetVersionResult
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.BucketURL,
+		uri:     "/?versioning",
+		method:  http.MethodGet,
+		result:  &res,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return &res, 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
new file mode 100644
index 0000000000000000000000000000000000000000..adc381fafc0a27f96de1dd43d9201973d20c9b9c
--- /dev/null
+++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/cos.go
@@ -0,0 +1,346 @@
+package cos
+
+import (
+	"bytes"
+	"context"
+	"encoding/base64"
+	"encoding/xml"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"net/http"
+	"net/url"
+	"reflect"
+	"text/template"
+
+	"strconv"
+
+	"github.com/google/go-querystring/query"
+	"github.com/mozillazg/go-httpheader"
+)
+
+const (
+	// Version current go sdk version
+	Version               = "0.7.3"
+	userAgent             = "cos-go-sdk-v5/" + Version
+	contentTypeXML        = "application/xml"
+	defaultServiceBaseURL = "http://service.cos.myqcloud.com"
+)
+
+var bucketURLTemplate = template.Must(
+	template.New("bucketURLFormat").Parse(
+		"{{.Schema}}://{{.BucketName}}.cos.{{.Region}}.myqcloud.com",
+	),
+)
+
+// BaseURL 访问各 API 所需的基础 URL
+type BaseURL struct {
+	// 访问 bucket, object 相关 API 的基础 URL(不包含 path 部分): http://example.com
+	BucketURL *url.URL
+	// 访问 service API 的基础 URL(不包含 path 部分): http://example.com
+	ServiceURL *url.URL
+}
+
+// NewBucketURL 生成 BaseURL 所需的 BucketURL
+//
+//   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 {
+	schema := "https"
+	if !secure {
+		schema = "http"
+	}
+
+	w := bytes.NewBuffer(nil)
+	bucketURLTemplate.Execute(w, struct {
+		Schema     string
+		BucketName string
+		Region     string
+	}{
+		schema, bucketName, region,
+	})
+
+	u, _ := url.Parse(w.String())
+	return u
+}
+
+// Client is a client manages communication with the COS API.
+type Client struct {
+	client *http.Client
+
+	UserAgent string
+	BaseURL   *BaseURL
+
+	common service
+
+	Service *ServiceService
+	Bucket  *BucketService
+	Object  *ObjectService
+}
+
+type service struct {
+	client *Client
+}
+
+// NewClient returns a new COS API client.
+func NewClient(uri *BaseURL, httpClient *http.Client) *Client {
+	if httpClient == nil {
+		httpClient = &http.Client{}
+	}
+
+	baseURL := &BaseURL{}
+	if uri != nil {
+		baseURL.BucketURL = uri.BucketURL
+		baseURL.ServiceURL = uri.ServiceURL
+	}
+	if baseURL.ServiceURL == nil {
+		baseURL.ServiceURL, _ = url.Parse(defaultServiceBaseURL)
+	}
+
+	c := &Client{
+		client:    httpClient,
+		UserAgent: userAgent,
+		BaseURL:   baseURL,
+	}
+	c.common.client = c
+	c.Service = (*ServiceService)(&c.common)
+	c.Bucket = (*BucketService)(&c.common)
+	c.Object = (*ObjectService)(&c.common)
+	return c
+}
+
+func (c *Client) newRequest(ctx context.Context, baseURL *url.URL, uri, method string, body interface{}, optQuery interface{}, optHeader interface{}) (req *http.Request, err error) {
+	uri, err = addURLOptions(uri, optQuery)
+	if err != nil {
+		return
+	}
+	u, _ := url.Parse(uri)
+	urlStr := baseURL.ResolveReference(u).String()
+
+	var reader io.Reader
+	contentType := ""
+	contentMD5 := ""
+	if body != nil {
+		// 上传文件
+		if r, ok := body.(io.Reader); ok {
+			reader = r
+		} else {
+			b, err := xml.Marshal(body)
+			if err != nil {
+				return nil, err
+			}
+			contentType = contentTypeXML
+			reader = bytes.NewReader(b)
+			contentMD5 = base64.StdEncoding.EncodeToString(calMD5Digest(b))
+		}
+	}
+
+	req, err = http.NewRequest(method, urlStr, reader)
+	if err != nil {
+		return
+	}
+
+	req.Header, err = addHeaderOptions(req.Header, optHeader)
+	if err != nil {
+		return
+	}
+	if v := req.Header.Get("Content-Length"); req.ContentLength == 0 && v != "" && v != "0" {
+		req.ContentLength, _ = strconv.ParseInt(v, 10, 64)
+	}
+
+	if contentMD5 != "" {
+		req.Header["Content-MD5"] = []string{contentMD5}
+	}
+	if c.UserAgent != "" {
+		req.Header.Set("User-Agent", c.UserAgent)
+	}
+	if req.Header.Get("Content-Type") == "" && contentType != "" {
+		req.Header.Set("Content-Type", contentType)
+	}
+	return
+}
+
+func (c *Client) doAPI(ctx context.Context, req *http.Request, result interface{}, closeBody bool) (*Response, error) {
+	req = req.WithContext(ctx)
+
+	resp, err := c.client.Do(req)
+	if err != nil {
+		// If we got an error, and the context has been canceled,
+		// the context's error is probably more useful.
+		select {
+		case <-ctx.Done():
+			return nil, ctx.Err()
+		default:
+		}
+		return nil, err
+	}
+
+	defer func() {
+		if closeBody {
+			// Close the body to let the Transport reuse the connection
+			io.Copy(ioutil.Discard, resp.Body)
+			resp.Body.Close()
+		}
+	}()
+
+	response := newResponse(resp)
+
+	err = checkResponse(resp)
+	if err != nil {
+		// even though there was an error, we still return the response
+		// in case the caller wants to inspect it further
+		return response, err
+	}
+
+	if result != nil {
+		if w, ok := result.(io.Writer); ok {
+			io.Copy(w, resp.Body)
+		} else {
+			err = xml.NewDecoder(resp.Body).Decode(result)
+			if err == io.EOF {
+				err = nil // ignore EOF errors caused by empty response body
+			}
+		}
+	}
+
+	return response, err
+}
+
+type sendOptions struct {
+	// 基础 URL
+	baseURL *url.URL
+	// URL 中除基础 URL 外的剩余部分
+	uri string
+	// 请求方法
+	method string
+
+	body interface{}
+	// url 查询参数
+	optQuery interface{}
+	// http header 参数
+	optHeader interface{}
+	// 用 result 反序列化 resp.Body
+	result interface{}
+	// 是否禁用自动调用 resp.Body.Close()
+	// 自动调用 Close() 是为了能够重用连接
+	disableCloseBody bool
+}
+
+func (c *Client) send(ctx context.Context, opt *sendOptions) (resp *Response, err error) {
+	req, err := c.newRequest(ctx, opt.baseURL, opt.uri, opt.method, opt.body, opt.optQuery, opt.optHeader)
+	if err != nil {
+		return
+	}
+
+	resp, err = c.doAPI(ctx, req, opt.result, !opt.disableCloseBody)
+	if err != nil {
+		return
+	}
+	return
+}
+
+// addURLOptions adds the parameters in opt as URL query parameters to s. opt
+// must be a struct whose fields may contain "url" tags.
+func addURLOptions(s string, opt interface{}) (string, error) {
+	v := reflect.ValueOf(opt)
+	if v.Kind() == reflect.Ptr && v.IsNil() {
+		return s, nil
+	}
+
+	u, err := url.Parse(s)
+	if err != nil {
+		return s, err
+	}
+
+	qs, err := query.Values(opt)
+	if err != nil {
+		return s, err
+	}
+
+	// 保留原有的参数,并且放在前面。因为 cos 的 url 路由是以第一个参数作为路由的
+	// e.g. /?uploads
+	q := u.RawQuery
+	rq := qs.Encode()
+	if q != "" {
+		if rq != "" {
+			u.RawQuery = fmt.Sprintf("%s&%s", q, qs.Encode())
+		}
+	} else {
+		u.RawQuery = rq
+	}
+	return u.String(), nil
+}
+
+// addHeaderOptions adds the parameters in opt as Header fields to req. opt
+// must be a struct whose fields may contain "header" tags.
+func addHeaderOptions(header http.Header, opt interface{}) (http.Header, error) {
+	v := reflect.ValueOf(opt)
+	if v.Kind() == reflect.Ptr && v.IsNil() {
+		return header, nil
+	}
+
+	h, err := httpheader.Header(opt)
+	if err != nil {
+		return nil, err
+	}
+
+	for key, values := range h {
+		for _, value := range values {
+			header.Add(key, value)
+		}
+	}
+	return header, nil
+}
+
+// Owner defines Bucket/Object's owner
+type Owner struct {
+	UIN         string `xml:"uin,omitempty"`
+	ID          string `xml:",omitempty"`
+	DisplayName string `xml:",omitempty"`
+}
+
+// Initiator same to the Owner struct
+type Initiator Owner
+
+// Response API 响应
+type Response struct {
+	*http.Response
+}
+
+func newResponse(resp *http.Response) *Response {
+	return &Response{
+		Response: resp,
+	}
+}
+
+// ACLHeaderOptions is the option of ACLHeader
+type ACLHeaderOptions struct {
+	XCosACL              string `header:"x-cos-acl,omitempty" url:"-" xml:"-"`
+	XCosGrantRead        string `header:"x-cos-grant-read,omitempty" url:"-" xml:"-"`
+	XCosGrantWrite       string `header:"x-cos-grant-write,omitempty" url:"-" xml:"-"`
+	XCosGrantFullControl string `header:"x-cos-grant-full-control,omitempty" url:"-" xml:"-"`
+}
+
+// ACLGrantee is the param of ACLGrant
+type ACLGrantee struct {
+	Type        string `xml:"type,attr"`
+	UIN         string `xml:"uin,omitempty"`
+	URI         string `xml:"URI,omitempty"`
+	ID          string `xml:",omitempty"`
+	DisplayName string `xml:",omitempty"`
+	SubAccount  string `xml:"Subaccount,omitempty"`
+}
+
+// ACLGrant is the param of ACLXml
+type ACLGrant struct {
+	Grantee    *ACLGrantee
+	Permission string
+}
+
+// ACLXml is the ACL body struct
+type ACLXml struct {
+	XMLName           xml.Name `xml:"AccessControlPolicy"`
+	Owner             *Owner
+	AccessControlList []ACLGrant `xml:"AccessControlList>Grant,omitempty"`
+}
diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/doc.go b/vendor/github.com/tencentyun/cos-go-sdk-v5/doc.go
new file mode 100644
index 0000000000000000000000000000000000000000..99005ab3a1c28d153f1892a2f3198af4b16942ee
--- /dev/null
+++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/doc.go
@@ -0,0 +1,3 @@
+// Package cos is COS(Cloud Object Storage) Go SDK. The V5 version(XML API).
+// There are examples of using each API in the project's 'example' directory.
+package cos
diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/error.go b/vendor/github.com/tencentyun/cos-go-sdk-v5/error.go
new file mode 100644
index 0000000000000000000000000000000000000000..9d06194ebd2753cad84c39ecb882240d37af370e
--- /dev/null
+++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/error.go
@@ -0,0 +1,49 @@
+package cos
+
+import (
+	"encoding/xml"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+)
+
+// ErrorResponse 包含 API 返回的错误信息
+//
+// https://www.qcloud.com/document/product/436/7730
+type ErrorResponse struct {
+	XMLName   xml.Name       `xml:"Error"`
+	Response  *http.Response `xml:"-"`
+	Code      string
+	Message   string
+	Resource  string
+	RequestID string `header:"x-cos-request-id,omitempty" url:"-" xml:"-"`
+	TraceID   string `xml:"TraceId,omitempty"`
+}
+
+// Error returns the error msg
+func (r *ErrorResponse) Error() string {
+	RequestID := r.RequestID
+	if RequestID == "" {
+		RequestID = r.Response.Header.Get("X-Cos-Request-Id")
+	}
+	TraceID := r.TraceID
+	if TraceID == "" {
+		TraceID = r.Response.Header.Get("X-Cos-Trace-Id")
+	}
+	return fmt.Sprintf("%v %v: %d %v(Message: %v, RequestId: %v, TraceId: %v)",
+		r.Response.Request.Method, r.Response.Request.URL,
+		r.Response.StatusCode, r.Code, r.Message, RequestID, TraceID)
+}
+
+// 检查 response 是否是出错时的返回的 response
+func checkResponse(r *http.Response) error {
+	if c := r.StatusCode; 200 <= c && c <= 299 {
+		return nil
+	}
+	errorResponse := &ErrorResponse{Response: r}
+	data, err := ioutil.ReadAll(r.Body)
+	if err == nil && data != nil {
+		xml.Unmarshal(data, errorResponse)
+	}
+	return errorResponse
+}
diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/go.mod b/vendor/github.com/tencentyun/cos-go-sdk-v5/go.mod
new file mode 100644
index 0000000000000000000000000000000000000000..35afc5cbc130639aba3b4fc40f94380bfcf92398
--- /dev/null
+++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/go.mod
@@ -0,0 +1,10 @@
+module github.com/tencentyun/cos-go-sdk-v5
+
+go 1.12
+
+require (
+	github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409
+	github.com/google/go-querystring v1.0.0
+	github.com/mozillazg/go-httpheader v0.2.1
+	github.com/stretchr/testify v1.3.0
+)
diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/go.sum b/vendor/github.com/tencentyun/cos-go-sdk-v5/go.sum
new file mode 100644
index 0000000000000000000000000000000000000000..5ee5a1d23653eddc38a311baf8aeb94d1e9846ac
--- /dev/null
+++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/go.sum
@@ -0,0 +1,13 @@
+github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409 h1:DTQ/38ao/CfXsrK0cSAL+h4R/u0VVvfWLZEOlLwEROI=
+github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
+github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
+github.com/mozillazg/go-httpheader v0.2.1 h1:geV7TrjbL8KXSyvghnFm+NyTux/hxwueTSrwhe88TQQ=
+github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/helper.go b/vendor/github.com/tencentyun/cos-go-sdk-v5/helper.go
new file mode 100644
index 0000000000000000000000000000000000000000..08c5a35df8d203bc545212794513ccccc90be4de
--- /dev/null
+++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/helper.go
@@ -0,0 +1,85 @@
+package cos
+
+import (
+	"bytes"
+	"crypto/md5"
+	"crypto/sha1"
+	"fmt"
+	"net/http"
+)
+
+// 计算 md5 或 sha1 时的分块大小
+const calDigestBlockSize = 1024 * 1024 * 10
+
+func calMD5Digest(msg []byte) []byte {
+	// TODO: 分块计算,减少内存消耗
+	m := md5.New()
+	m.Write(msg)
+	return m.Sum(nil)
+}
+
+func calSHA1Digest(msg []byte) []byte {
+	// TODO: 分块计算,减少内存消耗
+	m := sha1.New()
+	m.Write(msg)
+	return m.Sum(nil)
+}
+
+// cloneRequest returns a clone of the provided *http.Request. The clone is a
+// shallow copy of the struct and its Header map.
+func cloneRequest(r *http.Request) *http.Request {
+	// shallow copy of the struct
+	r2 := new(http.Request)
+	*r2 = *r
+	// deep copy of the Header
+	r2.Header = make(http.Header, len(r.Header))
+	for k, s := range r.Header {
+		r2.Header[k] = append([]string(nil), s...)
+	}
+	return r2
+}
+
+// encodeURIComponent like same function in javascript
+//
+// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
+//
+// http://www.ecma-international.org/ecma-262/6.0/#sec-uri-syntax-and-semantics
+func encodeURIComponent(s string) string {
+	var b bytes.Buffer
+	written := 0
+
+	for i, n := 0, len(s); i < n; i++ {
+		c := s[i]
+
+		switch c {
+		case '-', '_', '.', '!', '~', '*', '\'', '(', ')':
+			continue
+		default:
+			// Unreserved according to RFC 3986 sec 2.3
+			if 'a' <= c && c <= 'z' {
+
+				continue
+
+			}
+			if 'A' <= c && c <= 'Z' {
+
+				continue
+
+			}
+			if '0' <= c && c <= '9' {
+
+				continue
+			}
+		}
+
+		b.WriteString(s[written:i])
+		fmt.Fprintf(&b, "%%%02X", c)
+		written = i + 1
+	}
+
+	if written == 0 {
+		return s
+	}
+	b.WriteString(s[written:])
+	return b.String()
+}
diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/object.go b/vendor/github.com/tencentyun/cos-go-sdk-v5/object.go
new file mode 100644
index 0000000000000000000000000000000000000000..c88dc02d579a4b8885e118d3a8397d3fca9af01d
--- /dev/null
+++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/object.go
@@ -0,0 +1,605 @@
+package cos
+
+import (
+	"context"
+	"encoding/xml"
+	"errors"
+	"fmt"
+	"io"
+	"net/http"
+	"net/url"
+	"os"
+	"sort"
+	"time"
+)
+
+// ObjectService 相关 API
+type ObjectService service
+
+// ObjectGetOptions is the option of GetObject
+type ObjectGetOptions struct {
+	ResponseContentType        string `url:"response-content-type,omitempty" header:"-"`
+	ResponseContentLanguage    string `url:"response-content-language,omitempty" header:"-"`
+	ResponseExpires            string `url:"response-expires,omitempty" header:"-"`
+	ResponseCacheControl       string `url:"response-cache-control,omitempty" header:"-"`
+	ResponseContentDisposition string `url:"response-content-disposition,omitempty" header:"-"`
+	ResponseContentEncoding    string `url:"response-content-encoding,omitempty" header:"-"`
+	Range                      string `url:"-" header:"Range,omitempty"`
+	IfModifiedSince            string `url:"-" header:"If-Modified-Since,omitempty"`
+}
+
+// presignedURLTestingOptions is the opt of presigned url
+type presignedURLTestingOptions struct {
+	authTime *AuthTime
+}
+
+// Get Object 请求可以将一个文件(Object)下载至本地。
+// 该操作需要对目标 Object 具有读权限或目标 Object 对所有人都开放了读权限(公有读)。
+//
+// https://www.qcloud.com/document/product/436/7753
+func (s *ObjectService) Get(ctx context.Context, name string, opt *ObjectGetOptions, id ...string) (*Response, error) {
+	var u string
+	if len(id) == 1 {
+		u = fmt.Sprintf("/%s?versionId=%s", encodeURIComponent(name), id[0])
+	} else if len(id) == 0 {
+		u = "/" + encodeURIComponent(name)
+	} else {
+		return nil, errors.New("wrong params")
+	}
+
+	sendOpt := sendOptions{
+		baseURL:          s.client.BaseURL.BucketURL,
+		uri:              u,
+		method:           http.MethodGet,
+		optQuery:         opt,
+		optHeader:        opt,
+		disableCloseBody: true,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return resp, err
+}
+
+// GetToFile download the object to local file
+func (s *ObjectService) GetToFile(ctx context.Context, name, localpath string, opt *ObjectGetOptions, id ...string) (*Response, error) {
+	resp, err := s.Get(ctx, name, opt, id...)
+	if err != nil {
+		return resp, err
+	}
+	defer resp.Body.Close()
+
+	// If file exist, overwrite it
+	fd, err := os.OpenFile(localpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0660)
+	if err != nil {
+		return resp, err
+	}
+
+	_, err = io.Copy(fd, resp.Body)
+	fd.Close()
+	if err != nil {
+		return resp, err
+	}
+
+	return resp, nil
+}
+
+// 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) {
+	sendOpt := sendOptions{
+		baseURL:   s.client.BaseURL.BucketURL,
+		uri:       "/" + encodeURIComponent(name),
+		method:    httpMethod,
+		optQuery:  opt,
+		optHeader: opt,
+	}
+	req, err := s.client.newRequest(ctx, sendOpt.baseURL, sendOpt.uri, sendOpt.method, sendOpt.body, sendOpt.optQuery, sendOpt.optHeader)
+	if err != nil {
+		return nil, err
+	}
+
+	var authTime *AuthTime
+	if opt != nil {
+		if opt, ok := opt.(*presignedURLTestingOptions); ok {
+			authTime = opt.authTime
+		}
+	}
+	if authTime == nil {
+		authTime = NewAuthTime(expired)
+	}
+	authorization := newAuthorization(ak, sk, req, authTime)
+	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
+
+}
+
+// ObjectPutHeaderOptions the options of header of the put object
+type ObjectPutHeaderOptions struct {
+	CacheControl       string `header:"Cache-Control,omitempty" url:"-"`
+	ContentDisposition string `header:"Content-Disposition,omitempty" url:"-"`
+	ContentEncoding    string `header:"Content-Encoding,omitempty" url:"-"`
+	ContentType        string `header:"Content-Type,omitempty" url:"-"`
+	ContentMD5         string `header:"Content-MD5,omitempty" url:"-"`
+	ContentLength      int    `header:"Content-Length,omitempty" url:"-"`
+	Expect             string `header:"Expect,omitempty" url:"-"`
+	Expires            string `header:"Expires,omitempty" url:"-"`
+	XCosContentSHA1    string `header:"x-cos-content-sha1,omitempty" url:"-"`
+	// 自定义的 x-cos-meta-* header
+	XCosMetaXXX      *http.Header `header:"x-cos-meta-*,omitempty" url:"-"`
+	XCosStorageClass string       `header:"x-cos-storage-class,omitempty" url:"-"`
+	// 可选值: Normal, Appendable
+	//XCosObjectType string `header:"x-cos-object-type,omitempty" url:"-"`
+	// Enable Server Side Encryption, Only supported: AES256
+	XCosServerSideEncryption string `header:"x-cos-server-side-encryption,omitempty" url:"-" xml:"-"`
+}
+
+// ObjectPutOptions the options of put object
+type ObjectPutOptions struct {
+	*ACLHeaderOptions       `header:",omitempty" url:"-" xml:"-"`
+	*ObjectPutHeaderOptions `header:",omitempty" url:"-" xml:"-"`
+}
+
+// Put Object请求可以将一个文件(Oject)上传至指定Bucket。
+//
+// 当 r 不是 bytes.Buffer/bytes.Reader/strings.Reader 时,必须指定 opt.ObjectPutHeaderOptions.ContentLength
+//
+// https://www.qcloud.com/document/product/436/7749
+func (s *ObjectService) Put(ctx context.Context, name string, r io.Reader, opt *ObjectPutOptions) (*Response, error) {
+	sendOpt := sendOptions{
+		baseURL:   s.client.BaseURL.BucketURL,
+		uri:       "/" + encodeURIComponent(name),
+		method:    http.MethodPut,
+		body:      r,
+		optHeader: opt,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return resp, err
+}
+
+// PutFromFile put object from local file
+// Notice that when use this put large file need set non-body of debug req/resp, otherwise will out of memory
+func (s *ObjectService) PutFromFile(ctx context.Context, name string, filePath string, opt *ObjectPutOptions) (*Response, error) {
+	fd, err := os.Open(filePath)
+	if err != nil {
+		return nil, err
+	}
+	defer fd.Close()
+
+	return s.Put(ctx, name, fd, opt)
+}
+
+// ObjectCopyHeaderOptions is the head option of the Copy
+type ObjectCopyHeaderOptions struct {
+	// When use replace directive to update meta infos
+	CacheControl                    string `header:"Cache-Control,omitempty" url:"-"`
+	ContentDisposition              string `header:"Content-Disposition,omitempty" url:"-"`
+	ContentEncoding                 string `header:"Content-Encoding,omitempty" url:"-"`
+	ContentType                     string `header:"Content-Type,omitempty" url:"-"`
+	Expires                         string `header:"Expires,omitempty" url:"-"`
+	Expect                          string `header:"Expect,omitempty" url:"-"`
+	XCosMetadataDirective           string `header:"x-cos-metadata-directive,omitempty" url:"-" xml:"-"`
+	XCosCopySourceIfModifiedSince   string `header:"x-cos-copy-source-If-Modified-Since,omitempty" url:"-" xml:"-"`
+	XCosCopySourceIfUnmodifiedSince string `header:"x-cos-copy-source-If-Unmodified-Since,omitempty" url:"-" xml:"-"`
+	XCosCopySourceIfMatch           string `header:"x-cos-copy-source-If-Match,omitempty" url:"-" xml:"-"`
+	XCosCopySourceIfNoneMatch       string `header:"x-cos-copy-source-If-None-Match,omitempty" url:"-" xml:"-"`
+	XCosStorageClass                string `header:"x-cos-storage-class,omitempty" url:"-" xml:"-"`
+	// 自定义的 x-cos-meta-* header
+	XCosMetaXXX              *http.Header `header:"x-cos-meta-*,omitempty" url:"-"`
+	XCosCopySource           string       `header:"x-cos-copy-source" url:"-" xml:"-"`
+	XCosServerSideEncryption string       `header:"x-cos-server-side-encryption,omitempty" url:"-" xml:"-"`
+}
+
+// ObjectCopyOptions is the option of Copy, choose header or body
+type ObjectCopyOptions struct {
+	*ObjectCopyHeaderOptions `header:",omitempty" url:"-" xml:"-"`
+	*ACLHeaderOptions        `header:",omitempty" url:"-" xml:"-"`
+}
+
+// ObjectCopyResult is the result of Copy
+type ObjectCopyResult struct {
+	XMLName      xml.Name `xml:"CopyObjectResult"`
+	ETag         string   `xml:"ETag,omitempty"`
+	LastModified string   `xml:"LastModified,omitempty"`
+}
+
+// Copy 调用 PutObjectCopy 请求实现将一个文件从源路径复制到目标路径。建议文件大小 1M 到 5G,
+// 超过 5G 的文件请使用分块上传 Upload - Copy。在拷贝的过程中,文件元属性和 ACL 可以被修改。
+//
+// 用户可以通过该接口实现文件移动,文件重命名,修改文件属性和创建副本。
+//
+// 注意:在跨帐号复制的时候,需要先设置被复制文件的权限为公有读,或者对目标帐号赋权,同帐号则不需要。
+//
+// https://cloud.tencent.com/document/product/436/10881
+func (s *ObjectService) Copy(ctx context.Context, name, sourceURL string, opt *ObjectCopyOptions) (*ObjectCopyResult, *Response, error) {
+	var res ObjectCopyResult
+	if opt == nil {
+		opt = new(ObjectCopyOptions)
+	}
+	if opt.ObjectCopyHeaderOptions == nil {
+		opt.ObjectCopyHeaderOptions = new(ObjectCopyHeaderOptions)
+	}
+	opt.XCosCopySource = encodeURIComponent(sourceURL)
+
+	sendOpt := sendOptions{
+		baseURL:   s.client.BaseURL.BucketURL,
+		uri:       "/" + encodeURIComponent(name),
+		method:    http.MethodPut,
+		body:      nil,
+		optHeader: opt,
+		result:    &res,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	// If the error occurs during the copy operation, the error response is embedded in the 200 OK response. This means that a 200 OK response can contain either a success or an error.
+	if err == nil && resp.StatusCode == 200 {
+		if res.ETag == "" {
+			return &res, resp, errors.New("response 200 OK, but body contains an error")
+		}
+	}
+	return &res, resp, err
+}
+
+// Delete Object请求可以将一个文件(Object)删除。
+//
+// https://www.qcloud.com/document/product/436/7743
+func (s *ObjectService) Delete(ctx context.Context, name string) (*Response, error) {
+	// When use "" string might call the delete bucket interface
+	if len(name) == 0 {
+		return nil, errors.New("empty object name")
+	}
+
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.BucketURL,
+		uri:     "/" + encodeURIComponent(name),
+		method:  http.MethodDelete,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return resp, err
+}
+
+// ObjectHeadOptions is the option of HeadObject
+type ObjectHeadOptions struct {
+	IfModifiedSince string `url:"-" header:"If-Modified-Since,omitempty"`
+}
+
+// Head Object请求可以取回对应Object的元数据,Head的权限与Get的权限一致
+//
+// https://www.qcloud.com/document/product/436/7745
+func (s *ObjectService) Head(ctx context.Context, name string, opt *ObjectHeadOptions, id ...string) (*Response, error) {
+	var u string
+	if len(id) == 1 {
+		u = fmt.Sprintf("/%s?versionId=%s", encodeURIComponent(name), id[0])
+	} else if len(id) == 0 {
+		u = "/" + encodeURIComponent(name)
+	} else {
+		return nil, errors.New("wrong params")
+	}
+
+	sendOpt := sendOptions{
+		baseURL:   s.client.BaseURL.BucketURL,
+		uri:       u,
+		method:    http.MethodHead,
+		optHeader: opt,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	if resp != nil && resp.Header["X-Cos-Object-Type"] != nil && resp.Header["X-Cos-Object-Type"][0] == "appendable" {
+		resp.Header.Add("x-cos-next-append-position", resp.Header["Content-Length"][0])
+	}
+
+	return resp, err
+}
+
+// ObjectOptionsOptions is the option of object options
+type ObjectOptionsOptions struct {
+	Origin                      string `url:"-" header:"Origin"`
+	AccessControlRequestMethod  string `url:"-" header:"Access-Control-Request-Method"`
+	AccessControlRequestHeaders string `url:"-" header:"Access-Control-Request-Headers,omitempty"`
+}
+
+// Options Object请求实现跨域访问的预请求。即发出一个 OPTIONS 请求给服务器以确认是否可以进行跨域操作。
+//
+// 当CORS配置不存在时,请求返回403 Forbidden。
+//
+// https://www.qcloud.com/document/product/436/8288
+func (s *ObjectService) Options(ctx context.Context, name string, opt *ObjectOptionsOptions) (*Response, error) {
+	sendOpt := sendOptions{
+		baseURL:   s.client.BaseURL.BucketURL,
+		uri:       "/" + encodeURIComponent(name),
+		method:    http.MethodOptions,
+		optHeader: opt,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return resp, err
+}
+
+// CASJobParameters support three way: Standard(in 35 hours), Expedited(quick way, in 15 mins), Bulk(in 5-12 hours_
+type CASJobParameters struct {
+	Tier string `xml:"Tier"`
+}
+
+// ObjectRestoreOptions is the option of object restore
+type ObjectRestoreOptions struct {
+	XMLName xml.Name          `xml:"RestoreRequest"`
+	Days    int               `xml:"Days"`
+	Tier    *CASJobParameters `xml:"CASJobParameters"`
+}
+
+// PutRestore API can recover an object of type archived by COS archive.
+//
+// https://cloud.tencent.com/document/product/436/12633
+func (s *ObjectService) PostRestore(ctx context.Context, name string, opt *ObjectRestoreOptions) (*Response, error) {
+	u := fmt.Sprintf("/%s?restore", encodeURIComponent(name))
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.BucketURL,
+		uri:     u,
+		method:  http.MethodPost,
+		body:    opt,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+
+	return resp, err
+}
+
+// TODO Append 接口在优化未开放使用
+//
+// Append请求可以将一个文件(Object)以分块追加的方式上传至 Bucket 中。使用Append Upload的文件必须事前被设定为Appendable。
+// 当Appendable的文件被执行Put Object的操作以后,文件被覆盖,属性改变为Normal。
+//
+// 文件属性可以在Head Object操作中被查询到,当您发起Head Object请求时,会返回自定义Header『x-cos-object-type』,该Header只有两个枚举值:Normal或者Appendable。
+//
+// 追加上传建议文件大小1M - 5G。如果position的值和当前Object的长度不致,COS会返回409错误。
+// 如果Append一个Normal的Object,COS会返回409 ObjectNotAppendable。
+//
+// Appendable的文件不可以被复制,不参与版本管理,不参与生命周期管理,不可跨区域复制。
+//
+// 当 r 不是 bytes.Buffer/bytes.Reader/strings.Reader 时,必须指定 opt.ObjectPutHeaderOptions.ContentLength
+//
+// https://www.qcloud.com/document/product/436/7741
+// func (s *ObjectService) Append(ctx context.Context, name string, position int, r io.Reader, opt *ObjectPutOptions) (*Response, error) {
+// 	u := fmt.Sprintf("/%s?append&position=%d", encodeURIComponent(name), position)
+// 	if position != 0{
+// 		opt = nil
+// 	}
+// 	sendOpt := sendOptions{
+// 		baseURL:   s.client.BaseURL.BucketURL,
+// 		uri:       u,
+// 		method:    http.MethodPost,
+// 		optHeader: opt,
+// 		body:      r,
+// 	}
+// 	resp, err := s.client.send(ctx, &sendOpt)
+// 	return resp, err
+// }
+
+// ObjectDeleteMultiOptions is the option of DeleteMulti
+type ObjectDeleteMultiOptions struct {
+	XMLName xml.Name `xml:"Delete" header:"-"`
+	Quiet   bool     `xml:"Quiet" header:"-"`
+	Objects []Object `xml:"Object" header:"-"`
+	//XCosSha1 string `xml:"-" header:"x-cos-sha1"`
+}
+
+// ObjectDeleteMultiResult is the result of DeleteMulti
+type ObjectDeleteMultiResult struct {
+	XMLName        xml.Name `xml:"DeleteResult"`
+	DeletedObjects []Object `xml:"Deleted,omitempty"`
+	Errors         []struct {
+		Key     string
+		Code    string
+		Message string
+	} `xml:"Error,omitempty"`
+}
+
+// DeleteMulti 请求实现批量删除文件,最大支持单次删除1000个文件。
+// 对于返回结果,COS提供Verbose和Quiet两种结果模式。Verbose模式将返回每个Object的删除结果;
+// Quiet模式只返回报错的Object信息。
+// https://www.qcloud.com/document/product/436/8289
+func (s *ObjectService) DeleteMulti(ctx context.Context, opt *ObjectDeleteMultiOptions) (*ObjectDeleteMultiResult, *Response, error) {
+	var res ObjectDeleteMultiResult
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.BucketURL,
+		uri:     "/?delete",
+		method:  http.MethodPost,
+		body:    opt,
+		result:  &res,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return &res, resp, err
+}
+
+// Object is the meta info of the object
+type Object struct {
+	Key          string `xml:",omitempty"`
+	ETag         string `xml:",omitempty"`
+	Size         int    `xml:",omitempty"`
+	PartNumber   int    `xml:",omitempty"`
+	LastModified string `xml:",omitempty"`
+	StorageClass string `xml:",omitempty"`
+	Owner        *Owner `xml:",omitempty"`
+}
+
+// MultiUploadOptions is the option of the multiupload,
+// ThreadPoolSize default is one
+type MultiUploadOptions struct {
+	OptIni         *InitiateMultipartUploadOptions
+	PartSize       int64
+	ThreadPoolSize int
+}
+
+type Chunk struct {
+	Number int
+	OffSet int64
+	Size   int64
+}
+
+// jobs
+type Jobs struct {
+	Name       string
+	UploadId   string
+	FilePath   string
+	RetryTimes int
+	Chunk      Chunk
+	Data       io.Reader
+	Opt        *ObjectUploadPartOptions
+}
+
+type Results struct {
+	PartNumber int
+	Resp       *Response
+}
+
+func worker(s *ObjectService, jobs <-chan *Jobs, results chan<- *Results) {
+	for j := range jobs {
+		fd, err := os.Open(j.FilePath)
+		var res Results
+		if err != nil {
+			res.PartNumber = j.Chunk.Number
+			res.Resp = nil
+			results <- &res
+		}
+
+		fd.Seek(j.Chunk.OffSet, os.SEEK_SET)
+		// UploadPart do not support the chunk trsf, so need to add the content-length
+		opt := &ObjectUploadPartOptions{
+			ContentLength: int(j.Chunk.Size),
+		}
+
+		rt := j.RetryTimes
+		for {
+			resp, err := s.UploadPart(context.Background(), j.Name, j.UploadId, j.Chunk.Number,
+				&io.LimitedReader{R: fd, N: j.Chunk.Size}, opt)
+			res.PartNumber = j.Chunk.Number
+			res.Resp = resp
+			if err != nil {
+				rt--
+				if rt == 0 {
+					fd.Close()
+					results <- &res
+					break
+				}
+				continue
+			}
+			fd.Close()
+			results <- &res
+			break
+		}
+	}
+}
+
+func SplitFileIntoChunks(filePath string, partSize int64) ([]Chunk, int, error) {
+	if filePath == "" || partSize <= 0 {
+		return nil, 0, errors.New("chunkSize invalid")
+	}
+
+	file, err := os.Open(filePath)
+	if err != nil {
+		return nil, 0, err
+	}
+	defer file.Close()
+
+	stat, err := file.Stat()
+	if err != nil {
+		return nil, 0, err
+	}
+	var partNum = stat.Size() / partSize
+	// 10000 max part size
+	if partNum >= 10000 {
+		return nil, 0, errors.New("Too many parts, out of 10000")
+	}
+
+	var chunks []Chunk
+	var chunk = Chunk{}
+	for i := int64(0); i < partNum; i++ {
+		chunk.Number = int(i + 1)
+		chunk.OffSet = i * partSize
+		chunk.Size = partSize
+		chunks = append(chunks, chunk)
+	}
+
+	if stat.Size()%partSize > 0 {
+		chunk.Number = len(chunks) + 1
+		chunk.OffSet = int64(len(chunks)) * partSize
+		chunk.Size = stat.Size() % partSize
+		chunks = append(chunks, chunk)
+		partNum++
+	}
+
+	return chunks, int(partNum), nil
+
+}
+
+// MultiUpload 为高级upload接口,并发分块上传
+// 注意该接口目前只供参考
+//
+// 需要指定分块大小 partSize >= 1 ,单位为MB
+// 同时请确认分块数量不超过10000
+//
+
+func (s *ObjectService) MultiUpload(ctx context.Context, name string, filepath string, opt *MultiUploadOptions) (*CompleteMultipartUploadResult, *Response, error) {
+	// 1.Get the file chunk
+	bufSize := opt.PartSize * 1024 * 1024
+	chunks, partNum, err := SplitFileIntoChunks(filepath, bufSize)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	// 2.Init
+	optini := opt.OptIni
+	res, _, err := s.InitiateMultipartUpload(ctx, name, optini)
+	if err != nil {
+		return nil, nil, err
+	}
+	uploadID := res.UploadID
+	var poolSize int
+	if opt.ThreadPoolSize > 0 {
+		poolSize = opt.ThreadPoolSize
+	} else {
+		// Default is one
+		poolSize = 1
+	}
+
+	chjobs := make(chan *Jobs, 100)
+	chresults := make(chan *Results, 10000)
+	optcom := &CompleteMultipartUploadOptions{}
+
+	// 3.Start worker
+	for w := 1; w <= poolSize; w++ {
+		go worker(s, chjobs, chresults)
+	}
+
+	// 4.Push jobs
+	for _, chunk := range chunks {
+		job := &Jobs{
+			Name:       name,
+			RetryTimes: 3,
+			FilePath:   filepath,
+			UploadId:   uploadID,
+			Chunk:      chunk,
+		}
+		chjobs <- job
+	}
+	close(chjobs)
+
+	// 5.Recv the resp etag to complete
+	for i := 1; i <= partNum; i++ {
+		res := <-chresults
+		// Notice one part fail can not get the etag according.
+		if res.Resp == nil {
+			// Some part already fail, can not to get the header inside.
+			return nil, nil, fmt.Errorf("UploadID %s, part %d failed to get resp content.", uploadID, res.PartNumber)
+		}
+		// Notice one part fail can not get the etag according.
+		etag := res.Resp.Header.Get("ETag")
+		optcom.Parts = append(optcom.Parts, Object{
+			PartNumber: res.PartNumber, ETag: etag},
+		)
+	}
+	sort.Sort(ObjectList(optcom.Parts))
+
+	v, resp, err := s.CompleteMultipartUpload(context.Background(), name, uploadID, optcom)
+
+	return v, resp, err
+}
diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/object_acl.go b/vendor/github.com/tencentyun/cos-go-sdk-v5/object_acl.go
new file mode 100644
index 0000000000000000000000000000000000000000..2dc5935a4beba25354b05b83dca0dfa5e7d5567e
--- /dev/null
+++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/object_acl.go
@@ -0,0 +1,63 @@
+package cos
+
+import (
+	"context"
+	"net/http"
+)
+
+// ObjectGetACLResult is the result of GetObjectACL
+type ObjectGetACLResult ACLXml
+
+// GetACL Get Object ACL接口实现使用API读取Object的ACL表,只有所有者有权操作。
+//
+// https://www.qcloud.com/document/product/436/7744
+func (s *ObjectService) GetACL(ctx context.Context, name string) (*ObjectGetACLResult, *Response, error) {
+	var res ObjectGetACLResult
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.BucketURL,
+		uri:     "/" + encodeURIComponent(name) + "?acl",
+		method:  http.MethodGet,
+		result:  &res,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return &res, resp, err
+}
+
+// ObjectPutACLOptions the options of put object acl
+type ObjectPutACLOptions struct {
+	Header *ACLHeaderOptions `url:"-" xml:"-"`
+	Body   *ACLXml           `url:"-" header:"-"`
+}
+
+// PutACL 使用API写入Object的ACL表,您可以通过Header:"x-cos-acl", "x-cos-grant-read" ,
+// "x-cos-grant-write" ,"x-cos-grant-full-control"传入ACL信息,
+// 也可以通过body以XML格式传入ACL信息,但是只能选择Header和Body其中一种,否则,返回冲突。
+//
+// Put Object ACL是一个覆盖操作,传入新的ACL将覆盖原有ACL。只有所有者有权操作。
+//
+// "x-cos-acl":枚举值为public-read,private;public-read意味这个Object有公有读私有写的权限,
+// private意味这个Object有私有读写的权限。
+//
+// "x-cos-grant-read":意味被赋予权限的用户拥有该Object的读权限
+//
+// "x-cos-grant-write":意味被赋予权限的用户拥有该Object的写权限
+//
+// "x-cos-grant-full-control":意味被赋予权限的用户拥有该Object的读写权限
+//
+// https://www.qcloud.com/document/product/436/7748
+func (s *ObjectService) PutACL(ctx context.Context, name string, opt *ObjectPutACLOptions) (*Response, error) {
+	header := opt.Header
+	body := opt.Body
+	if body != nil {
+		header = nil
+	}
+	sendOpt := sendOptions{
+		baseURL:   s.client.BaseURL.BucketURL,
+		uri:       "/" + encodeURIComponent(name) + "?acl",
+		method:    http.MethodPut,
+		optHeader: header,
+		body:      body,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return 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
new file mode 100644
index 0000000000000000000000000000000000000000..6ef57735633d42082ff29e017b48d73a932aab7b
--- /dev/null
+++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/object_part.go
@@ -0,0 +1,191 @@
+package cos
+
+import (
+	"context"
+	"encoding/xml"
+	"errors"
+	"fmt"
+	"io"
+	"net/http"
+)
+
+// InitiateMultipartUploadOptions is the option of InitateMultipartUpload
+type InitiateMultipartUploadOptions struct {
+	*ACLHeaderOptions
+	*ObjectPutHeaderOptions
+}
+
+// InitiateMultipartUploadResult is the result of InitateMultipartUpload
+type InitiateMultipartUploadResult struct {
+	XMLName  xml.Name `xml:"InitiateMultipartUploadResult"`
+	Bucket   string
+	Key      string
+	UploadID string `xml:"UploadId"`
+}
+
+// InitiateMultipartUpload 请求实现初始化分片上传,成功执行此请求以后会返回Upload ID用于后续的Upload Part请求。
+//
+// https://www.qcloud.com/document/product/436/7746
+func (s *ObjectService) InitiateMultipartUpload(ctx context.Context, name string, opt *InitiateMultipartUploadOptions) (*InitiateMultipartUploadResult, *Response, error) {
+	var res InitiateMultipartUploadResult
+	sendOpt := sendOptions{
+		baseURL:   s.client.BaseURL.BucketURL,
+		uri:       "/" + encodeURIComponent(name) + "?uploads",
+		method:    http.MethodPost,
+		optHeader: opt,
+		result:    &res,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return &res, resp, err
+}
+
+// ObjectUploadPartOptions is the options of upload-part
+type ObjectUploadPartOptions struct {
+	Expect          string `header:"Expect,omitempty" url:"-"`
+	XCosContentSHA1 string `header:"x-cos-content-sha1" url:"-"`
+	ContentLength   int    `header:"Content-Length,omitempty" url:"-"`
+}
+
+// UploadPart 请求实现在初始化以后的分块上传,支持的块的数量为1到10000,块的大小为1 MB 到5 GB。
+// 在每次请求Upload Part时候,需要携带partNumber和uploadID,partNumber为块的编号,支持乱序上传。
+//
+// 当传入uploadID和partNumber都相同的时候,后传入的块将覆盖之前传入的块。当uploadID不存在时会返回404错误,NoSuchUpload.
+//
+// 当 r 不是 bytes.Buffer/bytes.Reader/strings.Reader 时,必须指定 opt.ContentLength
+//
+// https://www.qcloud.com/document/product/436/7750
+func (s *ObjectService) UploadPart(ctx context.Context, name, uploadID string, partNumber int, r io.Reader, opt *ObjectUploadPartOptions) (*Response, error) {
+	u := fmt.Sprintf("/%s?partNumber=%d&uploadId=%s", encodeURIComponent(name), partNumber, uploadID)
+	sendOpt := sendOptions{
+		baseURL:   s.client.BaseURL.BucketURL,
+		uri:       u,
+		method:    http.MethodPut,
+		optHeader: opt,
+		body:      r,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return resp, err
+}
+
+// ObjectListPartsOptions is the option of ListParts
+type ObjectListPartsOptions struct {
+	EncodingType     string `url:"Encoding-type,omitempty"`
+	MaxParts         string `url:"max-parts,omitempty"`
+	PartNumberMarker string `url:"part-number-marker,omitempty"`
+}
+
+// ObjectListPartsResult is the result of ListParts
+type ObjectListPartsResult struct {
+	XMLName              xml.Name `xml:"ListPartsResult"`
+	Bucket               string
+	EncodingType         string `xml:"Encoding-type,omitempty"`
+	Key                  string
+	UploadID             string     `xml:"UploadId"`
+	Initiator            *Initiator `xml:"Initiator,omitempty"`
+	Owner                *Owner     `xml:"Owner,omitempty"`
+	StorageClass         string
+	PartNumberMarker     string
+	NextPartNumberMarker string `xml:"NextPartNumberMarker,omitempty"`
+	MaxParts             string
+	IsTruncated          bool
+	Parts                []Object `xml:"Part,omitempty"`
+}
+
+// ListParts 用来查询特定分块上传中的已上传的块。
+//
+// https://www.qcloud.com/document/product/436/7747
+func (s *ObjectService) ListParts(ctx context.Context, name, uploadID string, opt *ObjectListPartsOptions) (*ObjectListPartsResult, *Response, error) {
+	u := fmt.Sprintf("/%s?uploadId=%s", encodeURIComponent(name), uploadID)
+	var res ObjectListPartsResult
+	sendOpt := sendOptions{
+		baseURL:  s.client.BaseURL.BucketURL,
+		uri:      u,
+		method:   http.MethodGet,
+		result:   &res,
+		optQuery: opt,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return &res, resp, err
+}
+
+// CompleteMultipartUploadOptions is the option of CompleteMultipartUpload
+type CompleteMultipartUploadOptions struct {
+	XMLName xml.Name `xml:"CompleteMultipartUpload"`
+	Parts   []Object `xml:"Part"`
+}
+
+// CompleteMultipartUploadResult is the result CompleteMultipartUpload
+type CompleteMultipartUploadResult struct {
+	XMLName  xml.Name `xml:"CompleteMultipartUploadResult"`
+	Location string
+	Bucket   string
+	Key      string
+	ETag     string
+}
+
+// ObjectList can used for sort the parts which needs in complete upload part
+// sort.Sort(cos.ObjectList(opt.Parts))
+type ObjectList []Object
+
+func (o ObjectList) Len() int {
+	return len(o)
+}
+
+func (o ObjectList) Swap(i, j int) {
+	o[i], o[j] = o[j], o[i]
+}
+
+func (o ObjectList) Less(i, j int) bool { // rewrite the Less method from small to big
+	return o[i].PartNumber < o[j].PartNumber
+}
+
+// CompleteMultipartUpload 用来实现完成整个分块上传。当您已经使用Upload Parts上传所有块以后,你可以用该API完成上传。
+// 在使用该API时,您必须在Body中给出每一个块的PartNumber和ETag,用来校验块的准确性。
+//
+// 由于分块上传的合并需要数分钟时间,因而当合并分块开始的时候,COS就立即返回200的状态码,在合并的过程中,
+// COS会周期性的返回空格信息来保持连接活跃,直到合并完成,COS会在Body中返回合并后块的内容。
+//
+// 当上传块小于1 MB的时候,在调用该请求时,会返回400 EntityTooSmall;
+// 当上传块编号不连续的时候,在调用该请求时,会返回400 InvalidPart;
+// 当请求Body中的块信息没有按序号从小到大排列的时候,在调用该请求时,会返回400 InvalidPartOrder;
+// 当UploadId不存在的时候,在调用该请求时,会返回404 NoSuchUpload。
+//
+// 建议您及时完成分块上传或者舍弃分块上传,因为已上传但是未终止的块会占用存储空间进而产生存储费用。
+//
+// https://www.qcloud.com/document/product/436/7742
+func (s *ObjectService) CompleteMultipartUpload(ctx context.Context, name, uploadID string, opt *CompleteMultipartUploadOptions) (*CompleteMultipartUploadResult, *Response, error) {
+	u := fmt.Sprintf("/%s?uploadId=%s", encodeURIComponent(name), uploadID)
+	var res CompleteMultipartUploadResult
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.BucketURL,
+		uri:     u,
+		method:  http.MethodPost,
+		body:    opt,
+		result:  &res,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	// If the error occurs during the copy operation, the error response is embedded in the 200 OK response. This means that a 200 OK response can contain either a success or an error.
+	if err == nil && resp.StatusCode == 200 {
+		if res.ETag == "" {
+			return &res, resp, errors.New("response 200 OK, but body contains an error")
+		}
+	}
+	return &res, resp, err
+}
+
+// AbortMultipartUpload 用来实现舍弃一个分块上传并删除已上传的块。当您调用Abort Multipart Upload时,
+// 如果有正在使用这个Upload Parts上传块的请求,则Upload Parts会返回失败。当该UploadID不存在时,会返回404 NoSuchUpload。
+//
+// 建议您及时完成分块上传或者舍弃分块上传,因为已上传但是未终止的块会占用存储空间进而产生存储费用。
+//
+// https://www.qcloud.com/document/product/436/7740
+func (s *ObjectService) AbortMultipartUpload(ctx context.Context, name, uploadID string) (*Response, error) {
+	u := fmt.Sprintf("/%s?uploadId=%s", encodeURIComponent(name), uploadID)
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.BucketURL,
+		uri:     u,
+		method:  http.MethodDelete,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return resp, err
+}
diff --git a/vendor/github.com/tencentyun/cos-go-sdk-v5/service.go b/vendor/github.com/tencentyun/cos-go-sdk-v5/service.go
new file mode 100644
index 0000000000000000000000000000000000000000..defe938aec1a5687048c20c48b2f8dc31ac5b337
--- /dev/null
+++ b/vendor/github.com/tencentyun/cos-go-sdk-v5/service.go
@@ -0,0 +1,35 @@
+package cos
+
+import (
+	"context"
+	"encoding/xml"
+	"net/http"
+)
+
+// Service 相关 API
+type ServiceService service
+
+// ServiceGetResult is the result of Get Service
+type ServiceGetResult struct {
+	XMLName xml.Name `xml:"ListAllMyBucketsResult"`
+	Owner   *Owner   `xml:"Owner"`
+	Buckets []Bucket `xml:"Buckets>Bucket,omitempty"`
+}
+
+// Get Service 接口实现获取该用户下所有Bucket列表。
+//
+// 该API接口需要使用Authorization签名认证,
+// 且只能获取签名中AccessID所属账户的Bucket列表。
+//
+// https://www.qcloud.com/document/product/436/8291
+func (s *ServiceService) Get(ctx context.Context) (*ServiceGetResult, *Response, error) {
+	var res ServiceGetResult
+	sendOpt := sendOptions{
+		baseURL: s.client.BaseURL.ServiceURL,
+		uri:     "/",
+		method:  http.MethodGet,
+		result:  &res,
+	}
+	resp, err := s.client.send(ctx, &sendOpt)
+	return &res, resp, err
+}
diff --git a/website/docs/backends/types/cos.html.md b/website/docs/backends/types/cos.html.md
new file mode 100644
index 0000000000000000000000000000000000000000..5c4ccc762d8265183f2407199b8910de256dd753
--- /dev/null
+++ b/website/docs/backends/types/cos.html.md
@@ -0,0 +1,61 @@
+---
+layout: "backend-types"
+page_title: "Backend Type: cos"
+sidebar_current: "docs-backends-types-standard-cos"
+description: |-
+  Terraform can store the state remotely, making it easier to version and work with in a team.
+---
+
+# COS
+
+**Kind: Standard (with locking)**
+
+Stores the state as an object in a configurable prefix in a given bucket on [Tencent Cloud Object Storage](https://intl.cloud.tencent.com/product/cos) (COS).
+This backend also supports [state locking](/docs/state/locking.html).
+
+~> **Warning!** It is highly recommended that you enable [Object Versioning](https://intl.cloud.tencent.com/document/product/436/19883)
+on the COS bucket to allow for state recovery in the case of accidental deletions and human error.
+
+## Example Configuration
+
+```hcl
+terraform {
+  backend "cos" {
+    region = "ap-guangzhou"
+    bucket = "bucket-for-terraform-state-1258798060"
+    prefix = "terraform/state"
+  }
+}
+```
+
+This assumes we have a [COS Bucket](https://www.terraform.io/docs/providers/tencentcloud/r/cos_bucket.html) created named `bucket-for-terraform-state-1258798060`,
+Terraform state will be written into the file `terraform/state/terraform.tfstate`.
+
+## Using the COS remote state
+
+To make use of the COS remote state we can use the [`terraform_remote_state` data source](/docs/providers/terraform/d/remote_state.html).
+
+```hcl
+data "terraform_remote_state" "foo" {
+  backend = "cos"
+
+  config = {
+    region = "ap-guangzhou"
+    bucket = "bucket-for-terraform-state-1258798060"
+    prefix = "terraform/state"
+  }
+}
+```
+
+## Configuration variables
+
+The following configuration options or environment variables are supported:
+
+ * `secret_id` - (Optional) Secret id of Tencent Cloud. It supports environment variables `TENCENTCLOUD_SECRET_ID`.
+ * `secret_key` - (Optional) Secret key of Tencent Cloud. It supports environment variables `TENCENTCLOUD_SECRET_KEY`.
+ * `region` - (Optional) The region of the COS bucket. It supports environment variables `TENCENTCLOUD_REGION`.
+ * `bucket` - (Required) The name of the COS bucket. You shall manually create it first.
+ * `prefix` - (Optional) The directory for saving the state file in bucket. Default to "env:".
+ * `key` - (Optional) The path for saving the state file in bucket. Defaults to `terraform.tfstate`.
+ * `encrypt` - (Optional) Whether to enable server side encryption of the state file. If it is true, COS will use 'AES256' encryption algorithm to encrypt state file.
+ * `acl` - (Optional) Object ACL to be applied to the state file, allows `private` and `public-read`. Defaults to `private`.
diff --git a/website/docs/state/workspaces.html.md b/website/docs/state/workspaces.html.md
index 28ea945b75273248d35e5e46c553f041487f3d29..899d5a5e4d384acbfe13ad508a4fe0873edb305a 100644
--- a/website/docs/state/workspaces.html.md
+++ b/website/docs/state/workspaces.html.md
@@ -27,6 +27,7 @@ Multiple workspaces are currently supported by the following backends:
 
  * [AzureRM](/docs/backends/types/azurerm.html)
  * [Consul](/docs/backends/types/consul.html)
+ * [COS](/docs/backends/types/cos.html)
  * [GCS](/docs/backends/types/gcs.html)
  * [Local](/docs/backends/types/local.html)
  * [Manta](/docs/backends/types/manta.html)
diff --git a/website/layouts/backend-types.erb b/website/layouts/backend-types.erb
index 315470890f34f0d7480889f4fb69249740f380a1..39f1200b68d2a93a9cfbb23dbbda77a9440186af 100644
--- a/website/layouts/backend-types.erb
+++ b/website/layouts/backend-types.erb
@@ -36,6 +36,9 @@
                   <li<%= sidebar_current("docs-backends-types-standard-consul") %>>
                     <a href="/docs/backends/types/consul.html">consul</a>
                   </li>
+                  <li<%= sidebar_current("docs-backends-types-standard-cos") %>>
+                    <a href="/docs/backends/types/cos.html">cos</a>
+                  </li>
                   <li<%= sidebar_current("docs-backends-types-standard-etcdv2") %>>
                     <a href="/docs/backends/types/etcd.html">etcd</a>
                   </li>