New Upstream Release - golang-github-denverdino-aliyungo

Ready changes

Summary

Merged new upstream version: 0.0~git20220905.589a058 (was: 0.0~git20180921.13fa8aa).

Resulting package

Built on 2022-09-08T12:08 (took 8m2s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-releases golang-github-denverdino-aliyungo-dev

Lintian Result

Diff

diff --git a/.circleci/config.yml b/.circleci/config.yml
index 18cd91b..1e75b62 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -2,7 +2,7 @@ version: 2
 jobs:
   build:
     docker:
-    - image: circleci/golang:1.8
+    - image: circleci/golang:1.10
     working_directory: /go/src/github.com/denverdino/aliyungo
     steps:
     - checkout
diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
new file mode 100644
index 0000000..78fafe7
--- /dev/null
+++ b/.github/workflows/go.yml
@@ -0,0 +1,31 @@
+name: Go
+on: [push]
+jobs:
+
+  build:
+    name: Build
+    runs-on: ubuntu-latest
+    steps:
+
+    - name: Set up Go 1.13
+      uses: actions/setup-go@v1
+      with:
+        go-version: 1.13
+      id: go
+
+    - name: Check out code into the Go module directory
+      uses: actions/checkout@v1
+
+    - name: Get dependencies
+      run: |
+        go get -v -t -d ./...
+        if [ -f Gopkg.toml ]; then
+            curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
+            dep ensure
+        fi
+    - name: Run golangci-lint with reviewdog
+      uses: reviewdog/action-golangci-lint@v1.1.3
+      with:
+        github_token: ${{ secrets.github_token }}
+
+
diff --git a/.travis.yml b/.travis.yml
index 269a0f3..fe08274 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,9 @@
 language: go
-
+arch:
+  - AMD64
+  - ppc64le
 go:
-  - 1.7.4
+  - 1.10.8
 
 # let us have speedy Docker-based Travis workers
 sudo: false
diff --git a/acm/acm.go b/acm/acm.go
index 4fe18d8..b179456 100644
--- a/acm/acm.go
+++ b/acm/acm.go
@@ -1,17 +1,17 @@
 package acm
 
 import (
-	"net/http"
-	"time"
-	"fmt"
+	"crypto/hmac"
+	"crypto/sha1"
+	"encoding/base64"
 	"errors"
+	"fmt"
 	"io/ioutil"
-	"strings"
-	"encoding/base64"
-	"crypto/sha1"
-	"crypto/hmac"
-	"strconv"
+	"net/http"
 	"net/url"
+	"strconv"
+	"strings"
+	"time"
 )
 
 type Client struct {
@@ -66,7 +66,7 @@ func (c *Client) initServer() error {
 	servers := strings.Split(body, "\n")
 
 	for k, v := range servers {
-		if strings.Index(v, ":") == -1 {
+		if !strings.Contains(v, ":") {
 			c.servers[k] = v + ":8080"
 		} else {
 			c.servers[k] = v
@@ -101,7 +101,7 @@ func (c *Client) callApi(api string, params map[string]string, method string) (s
 	timeStamp = timeStamp[:13]
 
 	spec := "?"
-	if strings.Index(api, "?") != -1 {
+	if strings.Contains(api, "?") {
 		spec = "&"
 	}
 
@@ -133,7 +133,12 @@ func (c *Client) callApi(api string, params map[string]string, method string) (s
 		request.Header.Add("Spas-Signature", c.getSign([]string{probe}))
 		c.HttpClient.Timeout = time.Duration(c.TimeOut+30) * time.Second
 	} else {
-		request.Header.Add("Spas-Signature", c.getSign([]string{c.NameSpace, params["group"], timeStamp}))
+		if group, exists := params["group"]; exists {
+			request.Header.Add("Spas-Signature", c.getSign([]string{c.NameSpace, group, timeStamp}))
+		} else {
+			request.Header.Add("Spas-Signature", c.getSign([]string{c.NameSpace, timeStamp}))
+		}
+
 	}
 
 	resp, err := c.HttpClient.Do(request)
@@ -148,11 +153,6 @@ func (c *Client) callApi(api string, params map[string]string, method string) (s
 		return "", err
 	}
 
-	byt, err = GbkToUtf8(byt)
-	if err != nil {
-		return "", err
-	}
-
 	body := string(byt)
 
 	if resp.StatusCode != 200 {
@@ -177,20 +177,17 @@ func (c *Client) GetAllConfigs(pageNo, pageSize int) (string, error) {
 	return c.callApi("diamond-server/basestone.do?method=getAllConfigByTenant", map[string]string{
 		"pageNo":   strconv.Itoa(pageNo),
 		"pageSize": strconv.Itoa(pageSize),
+		"tenant":   c.NameSpace,
+		"method":   "getAllConfigByTenant",
 	}, "GET")
 }
 
 func (c *Client) Publish(dataId, group, content string) (string, error) {
-	bt, err := Utf8ToGbk([]byte(content))
-	if err != nil {
-		return "", err
-	}
-
 	return c.callApi("diamond-server/basestone.do?method=syncUpdateAll", map[string]string{
 		"tenant":  c.NameSpace,
 		"dataId":  dataId,
 		"group":   group,
-		"content": string(bt),
+		"content": content,
 	}, "POST")
 }
 
diff --git a/acm/acm_test.go b/acm/acm_test.go
index 2083a9a..e4aae0f 100644
--- a/acm/acm_test.go
+++ b/acm/acm_test.go
@@ -1,10 +1,10 @@
 package acm
 
 import (
-	"testing"
+	"fmt"
 	"log"
 	"os"
-	"fmt"
+	"testing"
 )
 
 func getClient() *Client {
@@ -26,7 +26,7 @@ func RunWithTest(t *testing.T, test func(client *Client, t *testing.T)) {
 	client := getClient()
 	defer client.Delete("test", "test")
 
-	_, err := client.Publish("test", "test", "test")
+	_, err := client.Publish("test", "test", "test测试")
 
 	if err != nil {
 		t.Fatalf("pulish error:%s", err)
@@ -50,13 +50,25 @@ func TestClient_GetConfig(t *testing.T) {
 		if err != nil {
 			t.Error(err)
 		}
+		if ret != "test测试" {
+			t.Error("wrong respond content")
+		}
 		fmt.Println(ret)
 	})
 }
 
 func TestClient_Subscribe(t *testing.T) {
 	RunWithTest(t, func(client *Client, t *testing.T) {
-		_, err := client.Subscribe("test", "test","")
+		_, err := client.Subscribe("test", "test", "")
+		if err != nil {
+			t.Error(err)
+		}
+	})
+}
+
+func TestClient_GetAllConfig(t *testing.T) {
+	RunWithTest(t, func(client *Client, t *testing.T) {
+		_, err := client.GetAllConfigs(1, 200)
 		if err != nil {
 			t.Error(err)
 		}
diff --git a/cdn/auth/random_uuid.go b/cdn/auth/random_uuid.go
new file mode 100644
index 0000000..169a2b6
--- /dev/null
+++ b/cdn/auth/random_uuid.go
@@ -0,0 +1,97 @@
+package auth
+
+import (
+	"crypto/rand"
+	"fmt"
+	"io"
+	"os"
+	"syscall"
+	"time"
+)
+
+const (
+	// Bits is the number of bits in a UUID
+	Bits = 128
+
+	// Size is the number of bytes in a UUID
+	Size = Bits / 8
+
+	format = "%08x%04x%04x%04x%012x"
+)
+
+var (
+	// Loggerf can be used to override the default logging destination. Such
+	// log messages in this library should be logged at warning or higher.
+	Loggerf = func(format string, args ...interface{}) {}
+)
+
+// UUID represents a UUID value. UUIDs can be compared and set to other values
+// and accessed by byte.
+type UUID [Size]byte
+
+// GenerateUUID creates a new, version 4 uuid.
+func GenerateUUID() (u UUID) {
+	const (
+		// ensures we backoff for less than 450ms total. Use the following to
+		// select new value, in units of 10ms:
+		// 	n*(n+1)/2 = d -> n^2 + n - 2d -> n = (sqrt(8d + 1) - 1)/2
+		maxretries = 9
+		backoff    = time.Millisecond * 10
+	)
+
+	var (
+		totalBackoff time.Duration
+		count        int
+		retries      int
+	)
+
+	for {
+		// This should never block but the read may fail. Because of this,
+		// we just try to read the random number generator until we get
+		// something. This is a very rare condition but may happen.
+		b := time.Duration(retries) * backoff
+		time.Sleep(b)
+		totalBackoff += b
+
+		n, err := io.ReadFull(rand.Reader, u[count:])
+		if err != nil {
+			if retryOnError(err) && retries < maxretries {
+				count += n
+				retries++
+				Loggerf("error generating version 4 uuid, retrying: %v", err)
+				continue
+			}
+
+			// Any other errors represent a system problem. What did someone
+			// do to /dev/urandom?
+			panic(fmt.Errorf("error reading random number generator, retried for %v: %v", totalBackoff.String(), err))
+		}
+
+		break
+	}
+
+	u[6] = (u[6] & 0x0f) | 0x40 // set version byte
+	u[8] = (u[8] & 0x3f) | 0x80 // set high order byte 0b10{8,9,a,b}
+
+	return u
+}
+
+func (u UUID) String() string {
+	return fmt.Sprintf(format, u[:4], u[4:6], u[6:8], u[8:10], u[10:])
+}
+
+// retryOnError tries to detect whether or not retrying would be fruitful.
+func retryOnError(err error) bool {
+	switch err := err.(type) {
+	case *os.PathError:
+		return retryOnError(err.Err) // unpack the target error
+	case syscall.Errno:
+		if err == syscall.EPERM {
+			// EPERM represents an entropy pool exhaustion, a condition under
+			// which we backoff and retry.
+			return true
+		}
+	}
+
+	return false
+}
diff --git a/cdn/auth/random_uuid_test.go b/cdn/auth/random_uuid_test.go
new file mode 100644
index 0000000..d2cb1a4
--- /dev/null
+++ b/cdn/auth/random_uuid_test.go
@@ -0,0 +1,21 @@
+package auth
+
+import (
+	"testing"
+)
+
+const iterations = 1000
+
+func TestUUID4Generation(t *testing.T) {
+	for i := 0; i < iterations; i++ {
+		u := GenerateUUID()
+
+		if u[6]&0xf0 != 0x40 {
+			t.Fatalf("version byte not correctly set: %v, %08b %08b", u, u[6], u[6]&0xf0)
+		}
+
+		if u[8]&0xc0 != 0x80 {
+			t.Fatalf("top order 8th byte not correctly set: %v, %b", u, u[8])
+		}
+	}
+}
diff --git a/cdn/auth/sign_url.go b/cdn/auth/sign_url.go
new file mode 100644
index 0000000..211bd04
--- /dev/null
+++ b/cdn/auth/sign_url.go
@@ -0,0 +1,80 @@
+package auth
+
+import (
+	"crypto/md5"
+	"fmt"
+	"net/url"
+	"time"
+)
+
+// An URLSigner provides URL signing utilities to sign URLs for Aliyun CDN
+// resources.
+// authentication document: https://help.aliyun.com/document_detail/85117.html
+type URLSigner struct {
+	authType string
+	privKey  string
+}
+
+// NewURLSigner returns a new signer object.
+func NewURLSigner(authType string, privKey string) *URLSigner {
+	return &URLSigner{
+		authType: authType,
+		privKey:  privKey,
+	}
+}
+
+// Sign returns a signed aliyuncdn url based on authentication type
+func (s URLSigner) Sign(uri string, expires time.Time) (string, error) {
+	r, err := url.Parse(uri)
+	if err != nil {
+		return "", fmt.Errorf("unable to parse url: %s", uri)
+	}
+
+	switch s.authType {
+	case "a":
+		return aTypeSign(r, s.privKey, expires), nil
+	case "b":
+		return bTypeSign(r, s.privKey, expires), nil
+	case "c":
+		return cTypeSign(r, s.privKey, expires), nil
+	default:
+		return "", fmt.Errorf("invalid authentication type")
+	}
+}
+
+// sign by A type authentication method.
+// authentication document: https://help.aliyun.com/document_detail/85113.html
+func aTypeSign(r *url.URL, privateKey string, expires time.Time) string {
+	//rand is a random uuid without "-"
+	rand := GenerateUUID().String()
+	// not use, "0" by default
+	uid := "0"
+	secret := fmt.Sprintf("%s-%d-%s-%s-%s", r.Path, expires.Unix(), rand, uid, privateKey)
+	hashValue := md5.Sum([]byte(secret))
+	authKey := fmt.Sprintf("%d-%s-%s-%x", expires.Unix(), rand, uid, hashValue)
+	if r.RawQuery == "" {
+		return fmt.Sprintf("%s?auth_key=%s", r.String(), authKey)
+	}
+	return fmt.Sprintf("%s&auth_key=%s", r.String(), authKey)
+
+}
+
+// sign by B type authentication method.
+// authentication document: https://help.aliyun.com/document_detail/85114.html
+func bTypeSign(r *url.URL, privateKey string, expires time.Time) string {
+	formatExp := expires.Format("200601021504")
+	secret := privateKey + formatExp + r.Path
+	hashValue := md5.Sum([]byte(secret))
+	signURL := fmt.Sprintf("%s://%s/%s/%x%s?%s", r.Scheme, r.Host, formatExp, hashValue, r.Path, r.RawQuery)
+	return signURL
+}
+
+// sign by C type authentication method.
+// authentication document: https://help.aliyun.com/document_detail/85115.html
+func cTypeSign(r *url.URL, privateKey string, expires time.Time) string {
+	hexExp := fmt.Sprintf("%x", expires.Unix())
+	secret := privateKey + r.Path + hexExp
+	hashValue := md5.Sum([]byte(secret))
+	signURL := fmt.Sprintf("%s://%s/%x/%s%s?%s", r.Scheme, r.Host, hashValue, hexExp, r.Path, r.RawQuery)
+	return signURL
+}
diff --git a/cdn/auth/sign_url_test.go b/cdn/auth/sign_url_test.go
new file mode 100644
index 0000000..46291d4
--- /dev/null
+++ b/cdn/auth/sign_url_test.go
@@ -0,0 +1,53 @@
+package auth
+
+import (
+	"crypto/md5"
+	"fmt"
+	"net/url"
+	"reflect"
+	"testing"
+	"time"
+)
+
+var (
+	testSignTime = time.Unix(1541064730, 0)
+	testPrivKey  = "12345678"
+)
+
+func assertEqual(t *testing.T, name string, x, y interface{}) {
+	if !reflect.DeepEqual(x, y) {
+		t.Errorf("%s: Not equal! Expected='%v', Actual='%v'\n", name, x, y)
+		t.FailNow()
+	}
+}
+
+func TestAtypeAuth(t *testing.T) {
+	r, _ := url.Parse("https://example.com/a?foo=bar")
+	url := aTypeTest(r, testPrivKey, testSignTime)
+	assertEqual(t, "testTypeA", "https://example.com/a?foo=bar&auth_key=1541064730-0-0-f9dd5ed1e274ab4b1d5f5745344bf28b", url)
+}
+
+func TestBtypeAuth(t *testing.T) {
+	signer := NewURLSigner("b", testPrivKey)
+	url, _ := signer.Sign("https://example.com/a?foo=bar", testSignTime)
+	assertEqual(t, "testTypeB", "https://example.com/201811011732/3a19d83a89ccb00a73212420791b0123/a?foo=bar", url)
+}
+
+func TestCtypeAuth(t *testing.T) {
+	signer := NewURLSigner("c", testPrivKey)
+	url, _ := signer.Sign("https://example.com/a?foo=bar", testSignTime)
+	assertEqual(t, "testTypeC", "https://example.com/7d6b308ce87beb16d9dba32d741220f6/5bdac81a/a?foo=bar", url)
+}
+
+func aTypeTest(r *url.URL, privateKey string, expires time.Time) string {
+	//rand equals "0" in test case
+	rand := "0"
+	uid := "0"
+	secret := fmt.Sprintf("%s-%d-%s-%s-%s", r.Path, expires.Unix(), rand, uid, privateKey)
+	hashValue := md5.Sum([]byte(secret))
+	authKey := fmt.Sprintf("%d-%s-%s-%x", expires.Unix(), rand, uid, hashValue)
+	if r.RawQuery == "" {
+		return fmt.Sprintf("%s?auth_key=%s", r.String(), authKey)
+	}
+	return fmt.Sprintf("%s&auth_key=%s", r.String(), authKey)
+}
diff --git a/cdn/types.go b/cdn/types.go
index 85b746e..82a1406 100644
--- a/cdn/types.go
+++ b/cdn/types.go
@@ -9,7 +9,7 @@ import (
 const (
 	Web                       = "web"
 	Download                  = "download"
-	video                     = "video"
+	Video                     = "video"
 	LiveStream                = "liveStream"
 	Ipaddr                    = "ipaddr"
 	Domain                    = "domain"
@@ -27,7 +27,7 @@ const (
 	AccessControlMaxAge       = "Access-Control-Max-Age"
 )
 
-var CdnTypes = []string{Web, Download, video, LiveStream}
+var CdnTypes = []string{Web, Download, Video, LiveStream}
 var SourceTypes = []string{Ipaddr, Domain, OSS}
 var Scopes = []string{Domestic, Overseas, Global}
 var HeaderKeys = []string{ContentType, CacheControl, ContentDisposition, ContentLanguage, Expires, AccessControlAllowMethods, AccessControlAllowOrigin, AccessControlMaxAge}
diff --git a/cen/client.go b/cen/client.go
new file mode 100644
index 0000000..135d147
--- /dev/null
+++ b/cen/client.go
@@ -0,0 +1,76 @@
+package cen
+
+import (
+	"os"
+
+	"github.com/denverdino/aliyungo/common"
+)
+
+// Interval for checking status in WaitForXXX method
+const DefaultWaitForInterval = 5
+
+// Default timeout value for WaitForXXX method
+const DefaultTimeout = 60
+
+type Client struct {
+	common.Client
+}
+
+const (
+	// CENDefaultEndpoint is the default API endpoint of CEN services
+	CENDefaultEndpoint = "https://cbn.aliyuncs.com"
+	CENAPIVersion      = "2017-09-12"
+	CENServiceCode     = "cen"
+)
+
+// ---------------------------------------
+// NewCENClient creates a new instance of CEN client
+// ---------------------------------------
+func NewCENClient(accessKeyId, accessKeySecret string, regionID common.Region) *Client {
+	return NewCENClientWithSecurityToken(accessKeyId, accessKeySecret, "", regionID)
+}
+
+func NewCENClientWithSecurityToken(accessKeyId string, accessKeySecret string, securityToken string, regionID common.Region) *Client {
+	endpoint := os.Getenv("CEN_ENDPOINT")
+	if endpoint == "" {
+		endpoint = CENDefaultEndpoint
+	}
+
+	return NewCENClientWithEndpointAndSecurityToken(endpoint, accessKeyId, accessKeySecret, securityToken, regionID)
+}
+
+//only for Hangzhou Regional Domain
+func NewCENClientWithSecurityToken4RegionalDomain(accessKeyId string, accessKeySecret string, securityToken string, regionID common.Region) *Client {
+	endpoint := os.Getenv("CEN_ENDPOINT")
+	if endpoint == "" {
+		endpoint = CENDefaultEndpoint
+	}
+
+	return NewCENClientWithEndpointAndSecurityToken4RegionalDomain(endpoint, accessKeyId, accessKeySecret, securityToken, regionID)
+}
+
+func NewCENClientWithEndpointAndSecurityToken(endpoint string, accessKeyId string, accessKeySecret string, securityToken string, regionID common.Region) *Client {
+	client := &Client{}
+	client.WithEndpoint(endpoint).
+		WithVersion(CENAPIVersion).
+		WithAccessKeyId(accessKeyId).
+		WithAccessKeySecret(accessKeySecret).
+		WithSecurityToken(securityToken).
+		WithServiceCode(CENServiceCode).
+		WithRegionID(regionID).
+		InitClient()
+	return client
+}
+
+func NewCENClientWithEndpointAndSecurityToken4RegionalDomain(endpoint string, accessKeyId string, accessKeySecret string, securityToken string, regionID common.Region) *Client {
+	client := &Client{}
+	client.WithEndpoint(endpoint).
+		WithVersion(CENAPIVersion).
+		WithAccessKeyId(accessKeyId).
+		WithAccessKeySecret(accessKeySecret).
+		WithSecurityToken(securityToken).
+		WithServiceCode(CENServiceCode).
+		WithRegionID(regionID).
+		InitClient4RegionalDomain()
+	return client
+}
diff --git a/cen/route.go b/cen/route.go
new file mode 100644
index 0000000..5beff1e
--- /dev/null
+++ b/cen/route.go
@@ -0,0 +1,116 @@
+package cen
+
+import (
+	"log"
+
+	"github.com/denverdino/aliyungo/common"
+)
+
+type PublishRouteEntriesArgs struct {
+	CenId                     string
+	ChildInstanceId           string
+	ChildInstanceRegionId     string
+	ChildInstanceRouteTableId string
+	ChildInstanceType         string
+	DestinationCidrBlock      string
+}
+
+type DescribePublishedRouteEntriesArgs struct {
+	common.Pagination
+	CenId                     string
+	ChildInstanceId           string
+	ChildInstanceRegionId     string
+	ChildInstanceType         string
+	ChildInstanceRouteTableId string
+	DestinationCidrBlock      string
+}
+
+type DescribePublishedRouteEntriesResponse struct {
+	common.Response
+	common.PaginationResult
+	PublishedRouteEntries struct {
+		PublishedRouteEntry []PublishedRouteEntry
+	}
+}
+
+type ConflictStatus string
+type NextHopType string
+
+const (
+	ConflictStatusConflict   = ConflictStatus("conflict")
+	ConflictStatusOverflow   = ConflictStatus("overflow")
+	ConflictStatusProhibited = ConflictStatus("prohibited")
+)
+
+const (
+	NextHopTypeInstance        = NextHopType("Instance")
+	NextHopTypeHaVip           = NextHopType("HaVip")
+	NextHopTypeRouterInterface = NextHopType("RouterInterface")
+)
+
+type PublishStatus string
+
+const (
+	PublishStatusPublished    = PublishStatus("Published")
+	PublishStatusNotPublished = PublishStatus("NonPublished")
+)
+
+type RouteType string
+
+const (
+	RouteTypeSystem = RouteType("System")
+	RouteTypeCustom = RouteType("Custom")
+	RouteTypeBGP    = RouteType("BGP")
+)
+
+type PublishedRouteEntry struct {
+	ChildInstanceRouteTableId string
+	Conflicts                 struct {
+		Conflict []Conflict
+	}
+	DestinationCidrBlock string
+	NextHopId            string
+
+	NextHopType     string
+	OperationalMode bool
+	PublishStatus   string
+	RouteType       string
+}
+
+type Conflict struct {
+	DestinationCidrBlock string
+	InstanceId           string
+	InstanceType         string
+	RegionId             string
+	Status               string
+}
+
+// PublishRouteEntries publish route
+//
+// You can read doc at https://help.aliyun.com/document_detail/85470.html
+func (client *Client) PublishRouteEntries(args *PublishRouteEntriesArgs) error {
+	response := &common.Response{}
+	err := client.Invoke("PublishRouteEntries", args, response)
+	if err != nil {
+		log.Printf("PublishRouteEntries: %s, %s\n", response.RequestId, err.Error())
+	}
+	return err
+}
+
+// DescribePublishedRouteEntries describe published route
+//
+// You can read doc at https://help.aliyun.com/document_detail/85472.html
+func (client *Client) DescribePublishedRouteEntries(
+	args *DescribePublishedRouteEntriesArgs,
+) (response *DescribePublishedRouteEntriesResponse, err error) {
+
+	response = &DescribePublishedRouteEntriesResponse{}
+
+	err = client.Invoke("DescribePublishedRouteEntries", args, response)
+
+	if err != nil {
+		log.Printf("DescribePublishedRouteEntries: %v, %v\n", args, response)
+	}
+
+	return response, err
+}
diff --git a/cen/route_test.go b/cen/route_test.go
new file mode 100644
index 0000000..837bada
--- /dev/null
+++ b/cen/route_test.go
@@ -0,0 +1,50 @@
+package cen
+
+import (
+	"encoding/json"
+	"fmt"
+	"testing"
+)
+
+var (
+	ak  = ""
+	sec = ""
+)
+
+func TestDescribePublishedRoute(t *testing.T) {
+	client := NewCENClient(ak, sec, "cn-shanghai")
+	res, err := client.DescribePublishedRouteEntries(
+		&DescribePublishedRouteEntriesArgs{
+			CenId:                     "cen-qhu4rn3cknrg5o4qhl",
+			ChildInstanceType:         "VPC",
+			ChildInstanceRegionId:     "cn-shanghai",
+			ChildInstanceRouteTableId: "vtb-uf699blmsutb4wkbzqcmt",
+			ChildInstanceId:           "vpc-uf6ch2jfder4r0z51vtox",
+		},
+	)
+	if err != nil {
+		t.Errorf("describe: %s", err.Error())
+		t.FailNow()
+	}
+	fmt.Printf("Result: %+v", res)
+	b, err := json.MarshalIndent(res, "", "  ")
+	fmt.Printf("%s", b)
+}
+
+func TestPublishedRoute(t *testing.T) {
+	client := NewCENClient(ak, sec, "cn-shanghai")
+	err := client.PublishRouteEntries(
+		&PublishRouteEntriesArgs{
+			CenId:                     "cen-qhu4rn3cknrg5o4qhl",
+			ChildInstanceType:         "VPC",
+			ChildInstanceRegionId:     "cn-shanghai",
+			ChildInstanceRouteTableId: "vtb-uf6nco4vj87ly556c589f",
+			ChildInstanceId:           "vpc-uf6ch2jfder4r0z51vtox",
+			DestinationCidrBlock:      "192.168.0.0/26",
+		},
+	)
+	if err != nil {
+		t.Errorf("publish: %s", err.Error())
+		t.FailNow()
+	}
+}
diff --git a/cms/client.go b/cms/client.go
index ca8f4f1..1da269c 100644
--- a/cms/client.go
+++ b/cms/client.go
@@ -33,6 +33,14 @@ func (client *Client) SetDebug(debug bool) {
 	client.debug = debug
 }
 
+// SetTransport sets transport to the http client
+func (client *Client) SetTransport(transport http.RoundTripper) {
+	if client.httpClient == nil {
+		client.httpClient = &http.Client{}
+	}
+	client.httpClient.Transport = transport
+}
+
 const (
 	//TODO 旧的API,暂时保留
 	DefaultEndpoint = "http://alert.aliyuncs.com"
diff --git a/common/client.go b/common/client.go
index 6c00d1c..7b2ff81 100644
--- a/common/client.go
+++ b/common/client.go
@@ -5,8 +5,10 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
+	"github.com/opentracing/opentracing-go/ext"
 	"io/ioutil"
 	"log"
+	"net"
 	"net/http"
 	"net/url"
 	"os"
@@ -15,6 +17,7 @@ import (
 	"time"
 
 	"github.com/denverdino/aliyungo/util"
+	"github.com/opentracing/opentracing-go"
 )
 
 // RemovalPolicy.N add index to array item
@@ -38,6 +41,9 @@ type Client struct {
 	regionID        Region
 	businessInfo    string
 	userAgent       string
+	disableTrace    bool
+	span            opentracing.Span
+	logger          *Logger
 }
 
 // Initialize properties of a client instance
@@ -48,18 +54,7 @@ func (client *Client) Init(endpoint, version, accessKeyId, accessKeySecret strin
 		ak += "&"
 	}
 	client.AccessKeySecret = ak
-	client.debug = false
-	handshakeTimeout, err := strconv.Atoi(os.Getenv("TLSHandshakeTimeout"))
-	if err != nil {
-		handshakeTimeout = 0
-	}
-	if handshakeTimeout == 0 {
-		client.httpClient = &http.Client{}
-	} else {
-		t := &http.Transport{
-			TLSHandshakeTimeout: time.Duration(handshakeTimeout) * time.Second}
-		client.httpClient = &http.Client{Transport: t}
-	}
+	client.InitClient()
 	client.endpoint = endpoint
 	client.version = version
 }
@@ -71,20 +66,97 @@ func (client *Client) NewInit(endpoint, version, accessKeyId, accessKeySecret, s
 	client.regionID = regionID
 }
 
+// Initialize properties of a client instance including regionID
+//only for hz regional Domain
+func (client *Client) NewInit4RegionalDomain(endpoint, version, accessKeyId, accessKeySecret, serviceCode string, regionID Region) {
+	client.Init(endpoint, version, accessKeyId, accessKeySecret)
+	client.serviceCode = serviceCode
+	client.regionID = regionID
+
+	client.setEndpoint4RegionalDomain(client.regionID, client.serviceCode, client.AccessKeyId, client.AccessKeySecret, client.securityToken)
+}
+
 // Intialize client object when all properties are ready
 func (client *Client) InitClient() *Client {
 	client.debug = false
-	handshakeTimeout, err := strconv.Atoi(os.Getenv("TLSHandshakeTimeout"))
-	if err != nil {
-		handshakeTimeout = 0
+
+	// create DefaultTransport manully, because transport doesn't has clone method in go 1.10
+	t := &http.Transport{
+		Proxy: http.ProxyFromEnvironment,
+		DialContext: (&net.Dialer{
+			Timeout:   30 * time.Second,
+			KeepAlive: 30 * time.Second,
+			DualStack: true,
+		}).DialContext,
+		MaxIdleConns:          100,
+		IdleConnTimeout:       90 * time.Second,
+		TLSHandshakeTimeout:   10 * time.Second,
+		ExpectContinueTimeout: 1 * time.Second,
+	}
+
+	handshakeTimeoutStr, ok := os.LookupEnv("TLSHandshakeTimeout")
+	if ok {
+		handshakeTimeout, err := strconv.Atoi(handshakeTimeoutStr)
+		if err != nil {
+			log.Printf("Get TLSHandshakeTimeout from env error: %v.", err)
+		} else {
+			t.TLSHandshakeTimeout = time.Duration(handshakeTimeout) * time.Second
+		}
 	}
-	if handshakeTimeout == 0 {
-		client.httpClient = &http.Client{}
-	} else {
-		t := &http.Transport{
-			TLSHandshakeTimeout: time.Duration(handshakeTimeout) * time.Second}
-		client.httpClient = &http.Client{Transport: t}
+
+	responseHeaderTimeoutStr, ok := os.LookupEnv("ResponseHeaderTimeout")
+	if ok {
+		responseHeaderTimeout, err := strconv.Atoi(responseHeaderTimeoutStr)
+		if err != nil {
+			log.Printf("Get ResponseHeaderTimeout from env error: %v.", err)
+		} else {
+			t.ResponseHeaderTimeout = time.Duration(responseHeaderTimeout) * time.Second
+		}
+	}
+
+	expectContinueTimeoutStr, ok := os.LookupEnv("ExpectContinueTimeout")
+	if ok {
+		expectContinueTimeout, err := strconv.Atoi(expectContinueTimeoutStr)
+		if err != nil {
+			log.Printf("Get ExpectContinueTimeout from env error: %v.", err)
+		} else {
+			t.ExpectContinueTimeout = time.Duration(expectContinueTimeout) * time.Second
+		}
+	}
+
+	idleConnTimeoutStr, ok := os.LookupEnv("IdleConnTimeout")
+	if ok {
+		idleConnTimeout, err := strconv.Atoi(idleConnTimeoutStr)
+		if err != nil {
+			log.Printf("Get IdleConnTimeout from env error: %v.", err)
+		} else {
+			t.IdleConnTimeout = time.Duration(idleConnTimeout) * time.Second
+		}
 	}
+
+	client.httpClient = &http.Client{
+		Transport: t,
+	}
+
+	httpTimeoutStr, ok := os.LookupEnv("HttpTimeout")
+	if ok {
+		httpTimeout, err := strconv.Atoi(httpTimeoutStr)
+		if err != nil {
+			log.Printf("Get HttpTimeout from env error: %v.", err)
+		} else {
+			client.httpClient.Timeout = time.Duration(httpTimeout) * time.Second
+		}
+	}
+
+	return client
+}
+
+// Intialize client object when all properties are ready
+//only for regional domain hz
+func (client *Client) InitClient4RegionalDomain() *Client {
+	client.InitClient()
+	//set endpoint
+	client.setEndpoint4RegionalDomain(client.regionID, client.serviceCode, client.AccessKeyId, client.AccessKeySecret, client.securityToken)
 	return client
 }
 
@@ -105,9 +177,35 @@ func (client *Client) setEndpointByLocation(region Region, serviceCode, accessKe
 	locationClient := NewLocationClient(accessKeyId, accessKeySecret, securityToken)
 	locationClient.SetDebug(true)
 	ep := locationClient.DescribeOpenAPIEndpoint(region, serviceCode)
-	if ep == "" {
-		ep = loadEndpointFromFile(region, serviceCode)
+
+	if ep != "" {
+		client.endpoint = ep
+	}
+}
+
+// Get openapi endpoint accessed by ecs instance.
+// For some UnitRegions, the endpoint pattern is https://[product].[regionid].aliyuncs.com
+// For some CentralRegions, the endpoint pattern is  https://[product].vpc-proxy.aliyuncs.com
+// The other region, the endpoint pattern is https://[product]-vpc.[regionid].aliyuncs.com
+func (client *Client) setEndpoint4RegionalDomain(region Region, serviceCode, accessKeyId, accessKeySecret, securityToken string) {
+	if endpoint, ok := CentralDomainServices[serviceCode]; ok {
+		client.endpoint = fmt.Sprintf("https://%s", endpoint)
+		return
+	}
+	for _, service := range RegionalDomainServices {
+		if service == serviceCode {
+			if ep, ok := UnitRegions[region]; ok {
+				client.endpoint = fmt.Sprintf("https://%s.%s.aliyuncs.com", serviceCode, ep)
+				return
+			}
+
+			client.endpoint = fmt.Sprintf("https://%s%s.%s.aliyuncs.com", serviceCode, "-vpc", region)
+			return
+		}
 	}
+	locationClient := NewLocationClient(accessKeyId, accessKeySecret, securityToken)
+	locationClient.SetDebug(true)
+	ep := locationClient.DescribeOpenAPIEndpoint(region, serviceCode)
 
 	if ep != "" {
 		client.endpoint = ep
@@ -199,6 +297,18 @@ func (client *Client) WithUserAgent(userAgent string) *Client {
 	return client
 }
 
+// WithUserAgent sets user agent to the request/response message
+func (client *Client) WithDisableTrace(disableTrace bool) *Client {
+	client.SetDisableTrace(disableTrace)
+	return client
+}
+
+// WithUserAgent sets user agent to the request/response message
+func (client *Client) WithSpan(span opentracing.Span) *Client {
+	client.SetSpan(span)
+	return client
+}
+
 // ----------------------------------------------------
 // SetXXX methods
 // ----------------------------------------------------
@@ -208,6 +318,10 @@ func (client *Client) SetEndpoint(endpoint string) {
 	client.endpoint = endpoint
 }
 
+func (client *Client) GetEndpoint() string {
+	return client.endpoint
+}
+
 // SetEndpoint sets custom version
 func (client *Client) SetVersion(version string) {
 	client.version = version
@@ -257,6 +371,24 @@ func (client *Client) SetSecurityToken(securityToken string) {
 	client.securityToken = securityToken
 }
 
+// SetTransport sets transport to the http client
+func (client *Client) SetTransport(transport http.RoundTripper) {
+	if client.httpClient == nil {
+		client.httpClient = &http.Client{}
+	}
+	client.httpClient.Transport = transport
+}
+
+// SetDisableTrace close trace mode
+func (client *Client) SetDisableTrace(disableTrace bool) {
+	client.disableTrace = disableTrace
+}
+
+// SetSpan set the parent span
+func (client *Client) SetSpan(span opentracing.Span) {
+	client.span = span
+}
+
 func (client *Client) initEndpoint() error {
 	// if set any value to "CUSTOMIZED_ENDPOINT" could skip location service.
 	// example: export CUSTOMIZED_ENDPOINT=true
@@ -264,6 +396,10 @@ func (client *Client) initEndpoint() error {
 		return nil
 	}
 
+	if client.endpoint != "" {
+		return nil
+	}
+
 	if client.serviceCode != "" && client.regionID != "" {
 		endpoint := client.getEndpointByLocation()
 		if endpoint == "" {
@@ -275,15 +411,17 @@ func (client *Client) initEndpoint() error {
 }
 
 // Invoke sends the raw HTTP request for ECS services
-func (client *Client) Invoke(action string, args interface{}, response interface{}) error {
+func (client *Client) Invoke(action string, args interface{}, response interface{}) (err error) {
 	if err := client.ensureProperties(); err != nil {
 		return err
 	}
 
-	//init endpoint
-	if err := client.initEndpoint(); err != nil {
-		return err
-	}
+	// log request
+	fieldMap := make(map[string]string)
+	initLogMsg(fieldMap)
+	defer func() {
+		client.printLog(fieldMap, err)
+	}()
 
 	request := Request{}
 	request.init(client.version, action, client.AccessKeyId, client.securityToken, client.regionID)
@@ -305,23 +443,58 @@ func (client *Client) Invoke(action string, args interface{}, response interface
 
 	// TODO move to util and add build val flag
 	httpReq.Header.Set("X-SDK-Client", `AliyunGO/`+Version+client.businessInfo)
-
 	httpReq.Header.Set("User-Agent", httpReq.UserAgent()+" "+client.userAgent)
 
+	// Set tracer
+	var span opentracing.Span
+	if ok := opentracing.IsGlobalTracerRegistered(); ok && !client.disableTrace {
+		tracer := opentracing.GlobalTracer()
+		var rootCtx opentracing.SpanContext
+
+		if client.span != nil {
+			rootCtx = client.span.Context()
+		}
+
+		span = tracer.StartSpan(
+			"AliyunGO-"+request.Action,
+			opentracing.ChildOf(rootCtx),
+			opentracing.Tag{string(ext.Component), "AliyunGO"},
+			opentracing.Tag{"ActionName", request.Action})
+
+		defer span.Finish()
+		tracer.Inject(
+			span.Context(),
+			opentracing.HTTPHeaders,
+			opentracing.HTTPHeadersCarrier(httpReq.Header))
+	}
+
+	putMsgToMap(fieldMap, httpReq)
 	t0 := time.Now()
+	fieldMap["{start_time}"] = t0.Format("2006-01-02 15:04:05")
 	httpResp, err := client.httpClient.Do(httpReq)
 	t1 := time.Now()
+	fieldMap["{cost}"] = t1.Sub(t0).String()
 	if err != nil {
+		if span != nil {
+			ext.LogError(span, err)
+		}
 		return GetClientError(err)
 	}
+	fieldMap["{code}"] = strconv.Itoa(httpResp.StatusCode)
+	fieldMap["{res_headers}"] = TransToString(httpResp.Header)
 	statusCode := httpResp.StatusCode
 
 	if client.debug {
 		log.Printf("Invoke %s %s %d (%v)", ECSRequestMethod, requestURL, statusCode, t1.Sub(t0))
 	}
 
+	if span != nil {
+		ext.HTTPStatusCode.Set(span, uint16(httpResp.StatusCode))
+	}
+
 	defer httpResp.Body.Close()
 	body, err := ioutil.ReadAll(httpResp.Body)
+	fieldMap["{res_body}"] = string(body)
 
 	if err != nil {
 		return GetClientError(err)
@@ -330,12 +503,19 @@ func (client *Client) Invoke(action string, args interface{}, response interface
 	if client.debug {
 		var prettyJSON bytes.Buffer
 		err = json.Indent(&prettyJSON, body, "", "    ")
-		log.Println(string(prettyJSON.Bytes()))
+		if err != nil {
+			log.Printf("Failed in json.Indent: %v\n", err)
+		} else {
+			log.Printf("JSON body: %s\n", prettyJSON.String())
+		}
 	}
 
 	if statusCode >= 400 && statusCode <= 599 {
 		errorResponse := ErrorResponse{}
 		err = json.Unmarshal(body, &errorResponse)
+		if err != nil {
+			log.Printf("Failed in json.Unmarshal: %v\n", err)
+		}
 		ecsError := &Error{
 			ErrorResponse: errorResponse,
 			StatusCode:    statusCode,
@@ -353,11 +533,18 @@ func (client *Client) Invoke(action string, args interface{}, response interface
 }
 
 // Invoke sends the raw HTTP request for ECS services
-func (client *Client) InvokeByFlattenMethod(action string, args interface{}, response interface{}) error {
+func (client *Client) InvokeByFlattenMethod(action string, args interface{}, response interface{}) (err error) {
 	if err := client.ensureProperties(); err != nil {
 		return err
 	}
 
+	// log request
+	fieldMap := make(map[string]string)
+	initLogMsg(fieldMap)
+	defer func() {
+		client.printLog(fieldMap, err)
+	}()
+
 	//init endpoint
 	if err := client.initEndpoint(); err != nil {
 		return err
@@ -384,23 +571,58 @@ func (client *Client) InvokeByFlattenMethod(action string, args interface{}, res
 
 	// TODO move to util and add build val flag
 	httpReq.Header.Set("X-SDK-Client", `AliyunGO/`+Version+client.businessInfo)
-
 	httpReq.Header.Set("User-Agent", httpReq.UserAgent()+" "+client.userAgent)
 
+	// Set tracer
+	var span opentracing.Span
+	if ok := opentracing.IsGlobalTracerRegistered(); ok && !client.disableTrace {
+		tracer := opentracing.GlobalTracer()
+		var rootCtx opentracing.SpanContext
+
+		if client.span != nil {
+			rootCtx = client.span.Context()
+		}
+
+		span = tracer.StartSpan(
+			"AliyunGO-"+request.Action,
+			opentracing.ChildOf(rootCtx),
+			opentracing.Tag{string(ext.Component), "AliyunGO"},
+			opentracing.Tag{"ActionName", request.Action})
+
+		defer span.Finish()
+		tracer.Inject(
+			span.Context(),
+			opentracing.HTTPHeaders,
+			opentracing.HTTPHeadersCarrier(httpReq.Header))
+	}
+
+	putMsgToMap(fieldMap, httpReq)
 	t0 := time.Now()
+	fieldMap["{start_time}"] = t0.Format("2006-01-02 15:04:05")
 	httpResp, err := client.httpClient.Do(httpReq)
 	t1 := time.Now()
+	fieldMap["{cost}"] = t1.Sub(t0).String()
 	if err != nil {
+		if span != nil {
+			ext.LogError(span, err)
+		}
 		return GetClientError(err)
 	}
+	fieldMap["{code}"] = strconv.Itoa(httpResp.StatusCode)
+	fieldMap["{res_headers}"] = TransToString(httpResp.Header)
 	statusCode := httpResp.StatusCode
 
 	if client.debug {
 		log.Printf("Invoke %s %s %d (%v)", ECSRequestMethod, requestURL, statusCode, t1.Sub(t0))
 	}
 
+	if span != nil {
+		ext.HTTPStatusCode.Set(span, uint16(httpResp.StatusCode))
+	}
+
 	defer httpResp.Body.Close()
 	body, err := ioutil.ReadAll(httpResp.Body)
+	fieldMap["{res_body}"] = string(body)
 
 	if err != nil {
 		return GetClientError(err)
@@ -409,12 +631,18 @@ func (client *Client) InvokeByFlattenMethod(action string, args interface{}, res
 	if client.debug {
 		var prettyJSON bytes.Buffer
 		err = json.Indent(&prettyJSON, body, "", "    ")
-		log.Println(string(prettyJSON.Bytes()))
+		if err != nil {
+			log.Printf("Failed in json.Indent: %v\n", err)
+		}
+		log.Println(prettyJSON.String())
 	}
 
 	if statusCode >= 400 && statusCode <= 599 {
 		errorResponse := ErrorResponse{}
 		err = json.Unmarshal(body, &errorResponse)
+		if err != nil {
+			log.Printf("Failed in json.Unmarshal: %v\n", err)
+		}
 		ecsError := &Error{
 			ErrorResponse: errorResponse,
 			StatusCode:    statusCode,
@@ -434,15 +662,22 @@ func (client *Client) InvokeByFlattenMethod(action string, args interface{}, res
 // Invoke sends the raw HTTP request for ECS services
 //改进了一下上面那个方法,可以使用各种Http方法
 //2017.1.30 增加了一个path参数,用来拓展访问的地址
-func (client *Client) InvokeByAnyMethod(method, action, path string, args interface{}, response interface{}) error {
+func (client *Client) InvokeByAnyMethod(method, action, path string, args interface{}, response interface{}) (err error) {
 	if err := client.ensureProperties(); err != nil {
 		return err
 	}
 
+	// log request
+	fieldMap := make(map[string]string)
+	initLogMsg(fieldMap)
+	defer func() {
+		client.printLog(fieldMap, err)
+	}()
+
 	//init endpoint
-	if err := client.initEndpoint(); err != nil {
-		return err
-	}
+	//if err := client.initEndpoint(); err != nil {
+	//	return err
+	//}
 
 	request := Request{}
 	request.init(client.version, action, client.AccessKeyId, client.securityToken, client.regionID)
@@ -456,7 +691,6 @@ func (client *Client) InvokeByAnyMethod(method, action, path string, args interf
 	// Generate the request URL
 	var (
 		httpReq *http.Request
-		err     error
 	)
 	if method == http.MethodGet {
 		requestURL := client.endpoint + path + "?" + data.Encode()
@@ -476,20 +710,56 @@ func (client *Client) InvokeByAnyMethod(method, action, path string, args interf
 	httpReq.Header.Set("X-SDK-Client", `AliyunGO/`+Version+client.businessInfo)
 	httpReq.Header.Set("User-Agent", httpReq.Header.Get("User-Agent")+" "+client.userAgent)
 
+	// Set tracer
+	var span opentracing.Span
+	if ok := opentracing.IsGlobalTracerRegistered(); ok && !client.disableTrace {
+		tracer := opentracing.GlobalTracer()
+		var rootCtx opentracing.SpanContext
+
+		if client.span != nil {
+			rootCtx = client.span.Context()
+		}
+
+		span = tracer.StartSpan(
+			"AliyunGO-"+request.Action,
+			opentracing.ChildOf(rootCtx),
+			opentracing.Tag{string(ext.Component), "AliyunGO"},
+			opentracing.Tag{"ActionName", request.Action})
+
+		defer span.Finish()
+		tracer.Inject(
+			span.Context(),
+			opentracing.HTTPHeaders,
+			opentracing.HTTPHeadersCarrier(httpReq.Header))
+	}
+
+	putMsgToMap(fieldMap, httpReq)
 	t0 := time.Now()
+	fieldMap["{start_time}"] = t0.Format("2006-01-02 15:04:05")
 	httpResp, err := client.httpClient.Do(httpReq)
 	t1 := time.Now()
+	fieldMap["{cost}"] = t1.Sub(t0).String()
 	if err != nil {
+		if span != nil {
+			ext.LogError(span, err)
+		}
 		return GetClientError(err)
 	}
+	fieldMap["{code}"] = strconv.Itoa(httpResp.StatusCode)
+	fieldMap["{res_headers}"] = TransToString(httpResp.Header)
 	statusCode := httpResp.StatusCode
 
 	if client.debug {
 		log.Printf("Invoke %s %s %d (%v) %v", ECSRequestMethod, client.endpoint, statusCode, t1.Sub(t0), data.Encode())
 	}
 
+	if span != nil {
+		ext.HTTPStatusCode.Set(span, uint16(httpResp.StatusCode))
+	}
+
 	defer httpResp.Body.Close()
 	body, err := ioutil.ReadAll(httpResp.Body)
+	fieldMap["{res_body}"] = string(body)
 
 	if err != nil {
 		return GetClientError(err)
@@ -498,7 +768,7 @@ func (client *Client) InvokeByAnyMethod(method, action, path string, args interf
 	if client.debug {
 		var prettyJSON bytes.Buffer
 		err = json.Indent(&prettyJSON, body, "", "    ")
-		log.Println(string(prettyJSON.Bytes()))
+		log.Println(prettyJSON.String())
 	}
 
 	if statusCode >= 400 && statusCode <= 599 {
@@ -548,3 +818,15 @@ func GetCustomError(code, message string) error {
 		StatusCode: 400,
 	}
 }
+
+func putMsgToMap(fieldMap map[string]string, request *http.Request) {
+	fieldMap["{host}"] = request.Host
+	fieldMap["{method}"] = request.Method
+	fieldMap["{uri}"] = request.URL.RequestURI()
+	fieldMap["{pid}"] = strconv.Itoa(os.Getpid())
+	fieldMap["{version}"] = strings.Split(request.Proto, "/")[1]
+	hostname, _ := os.Hostname()
+	fieldMap["{hostname}"] = hostname
+	fieldMap["{req_headers}"] = TransToString(request.Header)
+	fieldMap["{target}"] = request.URL.Path + request.URL.RawQuery
+}
diff --git a/common/client_test.go b/common/client_test.go
index 034241f..6eb7863 100644
--- a/common/client_test.go
+++ b/common/client_test.go
@@ -1,6 +1,16 @@
 package common
 
-import "os"
+import (
+	"fmt"
+	"github.com/opentracing/opentracing-go"
+	"github.com/stretchr/testify/assert"
+	"github.com/uber/jaeger-client-go"
+	jaegercfg "github.com/uber/jaeger-client-go/config"
+	jaegerlog "github.com/uber/jaeger-client-go/log"
+	"net/http"
+	"os"
+	"testing"
+)
 
 var (
 	TestAccessKeyId     = os.Getenv("AccessKeyId")
@@ -18,3 +28,125 @@ func NewTestClientForDebug() *LocationClient {
 	}
 	return testDebugClient
 }
+
+func TestClient_SetTransport(t *testing.T) {
+	client := NewTestClientForDebug()
+	transport := &myTransport{}
+	client.SetTransport(transport)
+	if client.httpClient.Transport.(*myTransport) != transport {
+		t.Fail()
+	}
+}
+
+type myTransport struct{}
+
+func (m *myTransport) RoundTrip(req *http.Request) (*http.Response, error) {
+	return http.DefaultTransport.RoundTrip(req)
+}
+
+func Test_InitClient4RegionalDomain(t *testing.T) {
+
+	var tests = []struct {
+		service  string
+		version  string
+		endpoint string
+	}{
+		{"ecs", "2014-05-26", "https://ecs-cn-hangzhou.aliyuncs.com"},
+		{"pvtz", "2018-01-01", "https://pvtz.aliyuncs.com"},
+		{"slb", "2014-05-15", "https://slb.aliyuncs.com"},
+		{"vpc", "2016-04-28", "https://vpc.aliyuncs.com"},
+	}
+
+	for _, test := range tests {
+		for _, region := range ValidRegions {
+			if region == Qingdao || region == HangZhouFinance {
+				continue
+			}
+
+			client := &Client{}
+			client.SetDebug(true)
+			client.WithEndpoint(test.endpoint).
+				WithVersion(test.version).
+				WithAccessKeyId(TestAccessKeyId).
+				WithAccessKeySecret(TestAccessKeySecret).
+				WithSecurityToken(TestSecurityToken).
+				WithServiceCode(test.service).
+				WithRegionID(region).
+				InitClient4RegionalDomain()
+
+			if endpoint, ok := CentralDomainServices[test.service]; ok {
+				domain := fmt.Sprintf("https://%s", endpoint)
+				if client.endpoint != domain {
+					t.Fail()
+				}
+				continue
+			}
+
+			if ep, ok := UnitRegions[region]; ok {
+				domain := fmt.Sprintf("https://%s.%s.aliyuncs.com", test.service, ep)
+				if client.endpoint != domain {
+					t.Fail()
+				}
+				continue
+			}
+
+			domain := fmt.Sprintf("https://%s%s.%s.aliyuncs.com", test.service, "-vpc", region)
+			if client.endpoint != domain {
+				t.Fail()
+			}
+		}
+
+	}
+}
+
+func Test_InvokeTracer(t *testing.T) {
+	client := NewTestClientForDebug()
+	assert.NotNil(t, client)
+	args := &DescribeEndpointsArgs{
+		Id:          Hangzhou,
+		ServiceCode: "ecs",
+		Type:        "openAPI",
+	}
+	// not set global tracer
+	resp, err := client.DescribeEndpoints(args)
+	t.Log(resp)
+	assert.Nil(t, err)
+
+	//set global tracer, no root span
+	var cfg = jaegercfg.Configuration{
+		ServiceName: "client test",
+		Sampler: &jaegercfg.SamplerConfig{
+			Type:  jaeger.SamplerTypeConst,
+			Param: 1,
+		},
+		Reporter: &jaegercfg.ReporterConfig{
+			LogSpans: true,
+		},
+	}
+	jLogger := jaegerlog.StdLogger
+	tracer, closer, _ := cfg.NewTracer(
+		jaegercfg.Logger(jLogger),
+	)
+	opentracing.InitGlobalTracer(tracer)
+	resp, err = client.DescribeEndpoints(args)
+	t.Log(resp)
+	assert.Nil(t, err)
+
+	// set global tracer, with root span
+	parentSpan := tracer.StartSpan("root")
+	fmt.Println(parentSpan)
+	client.SetSpan(parentSpan)
+	resp, err = client.DescribeEndpoints(args)
+	t.Log(resp)
+	assert.Nil(t, err)
+
+	// set disable trace
+	client.SetDisableTrace(true)
+	client.SetSpan(parentSpan)
+	resp, err = client.DescribeEndpoints(args)
+	t.Log(resp)
+	assert.Nil(t, err)
+
+	parentSpan.Finish()
+	closer.Close()
+}
diff --git a/common/endpoint.go b/common/endpoint.go
index 9902403..cbe63ee 100644
--- a/common/endpoint.go
+++ b/common/endpoint.go
@@ -4,10 +4,11 @@ import (
 	"encoding/xml"
 	"fmt"
 	"io/ioutil"
+	"log"
 	"os"
 	"strings"
+	"sync"
 	"time"
-	"log"
 )
 
 const (
@@ -19,7 +20,9 @@ const (
 )
 
 var (
-	endpoints = make(map[Region]map[string]string)
+	//endpoints = make(map[Region]map[string]string)
+
+	endpoints = sync.Map{}
 
 	SpecailEnpoints = map[Region]map[string]string{
 		APNorthEast1: {
@@ -52,6 +55,12 @@ var (
 			"rds": "https://rds.eu-central-1.aliyuncs.com",
 			"vpc": "https://vpc.eu-central-1.aliyuncs.com",
 		},
+		EUWest1: {
+			"ecs": "https://ecs.eu-west-1.aliyuncs.com",
+			"slb": "https://slb.eu-west-1.aliyuncs.com",
+			"rds": "https://rds.eu-west-1.aliyuncs.com",
+			"vpc": "https://vpc.eu-west-1.aliyuncs.com",
+		},
 		Zhangjiakou: {
 			"ecs": "https://ecs.cn-zhangjiakou.aliyuncs.com",
 			"slb": "https://slb.cn-zhangjiakou.aliyuncs.com",
@@ -123,19 +132,22 @@ func (client *LocationClient) DescribeEndpoints(args *DescribeEndpointsArgs) (*D
 }
 
 func getProductRegionEndpoint(region Region, serviceCode string) string {
-	if sp, ok := endpoints[region]; ok {
-		if endpoint, ok := sp[serviceCode]; ok {
-			return endpoint
+
+	if sp, ok := endpoints.Load(region); ok {
+		spt, ok := sp.(*sync.Map)
+		if ok {
+			if endp, ok := spt.Load(serviceCode); ok {
+				return endp.(string)
+			}
 		}
 	}
-
 	return ""
 }
 
 func setProductRegionEndpoint(region Region, serviceCode string, endpoint string) {
-	endpoints[region] = map[string]string{
-		serviceCode: endpoint,
-	}
+	m := sync.Map{}
+	m.Store(serviceCode, endpoint)
+	endpoints.Store(region, &m)
 }
 
 func (client *LocationClient) DescribeOpenAPIEndpoint(region Region, serviceCode string) string {
@@ -161,7 +173,7 @@ func (client *LocationClient) DescribeOpenAPIEndpoint(region Region, serviceCode
 	}
 
 	if err != nil || endpoint == nil || len(endpoint.Endpoints.Endpoint) <= 0 {
-		log.Printf("aliyungo: can not get endpoint from service, use default. endpoint=[%v], error=[%v]\n",endpoint, err)
+		log.Printf("aliyungo: can not get endpoint from service, use default. endpoint=[%v], error=[%v]\n", endpoint, err)
 		return ""
 	}
 
diff --git a/common/endpoints.xml b/common/endpoints.xml
index 21f3a0b..26ea765 100644
--- a/common/endpoints.xml
+++ b/common/endpoints.xml
@@ -1336,6 +1336,17 @@
             <Product><ProductName>Slb</ProductName><DomainName>slb.eu-central-1.aliyuncs.com</DomainName></Product>
         </Products>
     </Endpoint>
+    <Endpoint name="eu-west-1">
+        <RegionIds><RegionId>eu-west-1</RegionId></RegionIds>
+        <Products>
+            <Product><ProductName>Rds</ProductName><DomainName>rds.eu-west-1.aliyuncs.com</DomainName></Product>
+            <Product><ProductName>Ecs</ProductName><DomainName>ecs.eu-west-1.aliyuncs.com</DomainName></Product>
+            <Product><ProductName>Vpc</ProductName><DomainName>vpc.eu-west-1.aliyuncs.com</DomainName></Product>
+            <Product><ProductName>Kms</ProductName><DomainName>kms.eu-west-1.aliyuncs.com</DomainName></Product>
+            <Product><ProductName>Cms</ProductName><DomainName>metrics.cn-hangzhou.aliyuncs.com</DomainName></Product>
+            <Product><ProductName>Slb</ProductName><DomainName>slb.eu-west-1.aliyuncs.com</DomainName></Product>
+        </Products>
+    </Endpoint>
     <Endpoint name="cn-zhangjiakou">
         <RegionIds><RegionId>cn-zhangjiakou</RegionId></RegionIds>
         <Products>
diff --git a/common/logger.go b/common/logger.go
new file mode 100644
index 0000000..779e0b6
--- /dev/null
+++ b/common/logger.go
@@ -0,0 +1,116 @@
+package common
+
+import (
+	"encoding/json"
+	"github.com/denverdino/aliyungo/common/utils"
+	"io"
+	"log"
+	"os"
+	"strings"
+	"time"
+)
+
+var logChannel string
+var defaultChannel = "AliyunGO"
+
+type Logger struct {
+	*log.Logger
+	formatTemplate string
+	isOpen         bool
+	lastLogMsg     string
+}
+
+var defaultLoggerTemplate = `{time} {channel}: "{method} {uri} HTTP/{version}" {code} {cost} {hostname}`
+var loggerParam = []string{"{time}", "{start_time}", "{ts}", "{channel}", "{pid}", "{host}", "{method}", "{uri}", "{version}", "{target}", "{hostname}", "{code}", "{error}", "{req_headers}", "{res_body}", "{res_headers}", "{cost}"}
+
+func initLogMsg(fieldMap map[string]string) {
+	for _, value := range loggerParam {
+		fieldMap[value] = ""
+	}
+}
+
+func (client *Client) SetLogger(level string, channel string, out io.Writer, template string) {
+	if level == "" {
+		level = "info"
+	}
+
+	logChannel = "AliyunGO"
+	if channel != "" {
+		logChannel = channel
+	}
+	log := log.New(out, "["+strings.ToUpper(level)+"]", log.Lshortfile)
+	if template == "" {
+		template = defaultLoggerTemplate
+	}
+
+	client.logger = &Logger{
+		Logger:         log,
+		formatTemplate: template,
+		isOpen:         true,
+	}
+}
+
+func (client *Client) GetLogger() *Logger {
+	return client.logger
+}
+
+func (client *Client) GetLoggerMsg() string {
+	if client.logger == nil {
+		client.SetLogger("", "", os.Stdout, "")
+	}
+	return client.logger.lastLogMsg
+}
+
+func (client *Client) OpenLogger() {
+	if client.logger == nil {
+		client.SetLogger("", "", os.Stdout, "")
+	}
+	client.logger.isOpen = true
+}
+
+func (client *Client) CloseLogger() {
+	if client.logger != nil {
+		client.logger.isOpen = false
+	}
+}
+
+func (client *Client) SetTemplate(template string) {
+	if client.logger == nil {
+		client.SetLogger("", "", os.Stdout, "")
+	}
+	client.logger.formatTemplate = template
+}
+
+func (client *Client) GetTemplate() string {
+	if client.logger == nil {
+		client.SetLogger("", "", os.Stdout, "")
+	}
+	return client.logger.formatTemplate
+}
+
+func TransToString(object interface{}) string {
+	byt, err := json.Marshal(object)
+	if err != nil {
+		return ""
+	}
+	return string(byt)
+}
+
+func (client *Client) printLog(fieldMap map[string]string, err error) {
+	if err != nil {
+		fieldMap["{error}"] = err.Error()
+	}
+	fieldMap["{time}"] = time.Now().Format("2006-01-02 15:04:05")
+	fieldMap["{ts}"] = utils.GetTimeInFormatISO8601()
+	fieldMap["{channel}"] = logChannel
+	if client.logger != nil {
+		logMsg := client.logger.formatTemplate
+		for key, value := range fieldMap {
+			logMsg = strings.Replace(logMsg, key, value, -1)
+		}
+		client.logger.lastLogMsg = logMsg
+		if client.logger.isOpen == true {
+			client.logger.Output(2, logMsg)
+		}
+	}
+}
diff --git a/common/logger_test.go b/common/logger_test.go
new file mode 100644
index 0000000..450686d
--- /dev/null
+++ b/common/logger_test.go
@@ -0,0 +1,31 @@
+package common
+
+import (
+	"bytes"
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+func Test_Client_Logger(t *testing.T) {
+	client := NewTestClientForDebug()
+	assert.NotNil(t, client)
+	args := &DescribeEndpointsArgs{
+		Id:          Hangzhou,
+		ServiceCode: "ecs",
+		Type:        "openAPI",
+	}
+	// without logger
+	resp, err := client.DescribeEndpoints(args)
+	t.Log(resp)
+	assert.Nil(t, err)
+
+	// with logger
+	wr := new(bytes.Buffer)
+	assert.Nil(t, err)
+	template := `{time} {channel}: {method} {host} {uri} HTTP/{version} {code} {cost} {hostname} {req_headers} {error} {res_body}`
+	client.SetLogger("", "openapi", wr, template)
+	resp, err = client.DescribeEndpoints(args)
+	t.Log(wr.String())
+	t.Log(resp)
+	assert.Nil(t, err)
+}
diff --git a/common/regions.go b/common/regions.go
index d0560db..eb2bb7c 100644
--- a/common/regions.go
+++ b/common/regions.go
@@ -14,6 +14,8 @@ const (
 	Zhangjiakou = Region("cn-zhangjiakou")
 	Huhehaote   = Region("cn-huhehaote")
 
+	Chengdu = Region("cn-chengdu")
+
 	APSouthEast1 = Region("ap-southeast-1")
 	APNorthEast1 = Region("ap-northeast-1")
 	APSouthEast2 = Region("ap-southeast-2")
@@ -28,9 +30,14 @@ const (
 	MEEast1 = Region("me-east-1")
 
 	EUCentral1 = Region("eu-central-1")
+	EUWest1    = Region("eu-west-1")
 
 	ShenZhenFinance = Region("cn-shenzhen-finance-1")
 	ShanghaiFinance = Region("cn-shanghai-finance-1")
+	HangZhouFinance = Region("cn-hangzhou-finance-1")
+
+	CNNorth2Gov1 = Region("cn-north-2-gov-1")
+	RUSWest1     = Region("rus-west-1")
 )
 
 var ValidRegions = []Region{
@@ -39,8 +46,8 @@ var ValidRegions = []Region{
 	APNorthEast1, APSouthEast1, APSouthEast2, APSouthEast3, APSouthEast5,
 	APSouth1,
 	MEEast1,
-	EUCentral1,
-	ShenZhenFinance, ShanghaiFinance,
+	EUCentral1, EUWest1,
+	ShenZhenFinance, ShanghaiFinance, HangZhouFinance, CNNorth2Gov1,
 }
 
 // IsValidRegion checks if r is an Ali supported region.
diff --git a/common/types.go b/common/types.go
index cf161f1..c713986 100644
--- a/common/types.go
+++ b/common/types.go
@@ -14,6 +14,66 @@ const (
 	PostPaid = InstanceChargeType("PostPaid")
 )
 
+var SpecialDeployedProducts = map[string]map[Region]interface{}{
+	"vpc": {
+		Hangzhou:     Hangzhou,
+		Shenzhen:     Shenzhen,
+		APSouthEast1: APSouthEast1,
+		USWest1:      USWest1,
+		USEast1:      USEast1,
+		Chengdu:      Chengdu,
+		Zhangjiakou:  Zhangjiakou,
+		Huhehaote:    Huhehaote,
+		APSouthEast3: APSouthEast3,
+		EUCentral1:   EUCentral1,
+		EUWest1:      EUWest1,
+		APSouth1:     APSouth1,
+		APNorthEast1: APNorthEast1,
+		APSouthEast5: APSouthEast5,
+		APSouthEast2: APSouthEast2,
+		MEEast1:      MEEast1,
+		CNNorth2Gov1: CNNorth2Gov1,
+	},
+}
+
+var CentralDomainServices = map[string]string{
+	"pvtz": "pvtz.vpc-proxy.aliyuncs.com",
+}
+
+var RegionalDomainServices = []string{
+	"ecs",
+	"vpc",
+	"slb",
+}
+
+// Unit-Domain of central product
+var UnitRegions = map[Region]interface{}{
+	Hangzhou:     Hangzhou,
+	Shenzhen:     Shenzhen,
+	APSouthEast1: APSouthEast1,
+	USWest1:      USWest1,
+	USEast1:      USEast1,
+	Chengdu:      Chengdu,
+	Zhangjiakou:  Zhangjiakou,
+	Huhehaote:    Huhehaote,
+	APSouthEast3: APSouthEast3,
+	EUCentral1:   EUCentral1,
+	EUWest1:      EUWest1,
+	APSouth1:     APSouth1,
+	APNorthEast1: APNorthEast1,
+	APSouthEast5: APSouthEast5,
+	APSouthEast2: APSouthEast2,
+	CNNorth2Gov1: CNNorth2Gov1,
+	//MEEast1:      MEEast1,
+	//RUSWest1:        RUSWest1,
+	//Beijing:         Beijing,
+	//Shanghai:        Shanghai,
+	//Hongkong:        Hongkong,
+	//ShanghaiFinance: ShanghaiFinance,
+	//ShenZhenFinance: ShenZhenFinance,
+	HangZhouFinance: Hangzhou,
+}
+
 type DescribeEndpointArgs struct {
 	Id          Region
 	ServiceCode string
diff --git a/common/utils/utils.go b/common/utils/utils.go
new file mode 100644
index 0000000..f1dc3eb
--- /dev/null
+++ b/common/utils/utils.go
@@ -0,0 +1,9 @@
+package utils
+
+import "time"
+
+func GetTimeInFormatISO8601() (timeStr string) {
+	gmt := time.FixedZone("GMT", 0)
+
+	return time.Now().In(gmt).Format("2006-01-02T15:04:05Z")
+}
diff --git a/cs/client.go b/cs/client.go
index 0a69633..e83158a 100644
--- a/cs/client.go
+++ b/cs/client.go
@@ -32,6 +32,8 @@ type Client struct {
 	debug           bool
 	userAgent       string
 	httpClient      *http.Client
+	sourceIp        string
+	secureTransport string
 }
 
 type PaginationResult struct {
@@ -76,6 +78,29 @@ func (client *Client) SetUserAgent(userAgent string) {
 	client.userAgent = userAgent
 }
 
+// SetEndpoint sets customer endpoint
+func (client *Client) SetEndpoint(endpoint string) {
+	client.endpoint = endpoint
+}
+
+// SetTransport sets transport to the http client
+func (client *Client) SetTransport(transport http.RoundTripper) {
+	if client.httpClient == nil {
+		client.httpClient = &http.Client{}
+	}
+	client.httpClient.Transport = transport
+}
+
+// SetSourceIp set the source ip
+func (client *Client) SetSourceIp(sourceIp string) {
+	client.sourceIp = sourceIp
+}
+
+// SetSecureTransport set the secure transport
+func (client *Client) SetSecureTransport(secureTransport string) {
+	client.secureTransport = secureTransport
+}
+
 type Request struct {
 	Method          string
 	URL             string
@@ -130,6 +155,12 @@ func (client *Client) Invoke(region common.Region, method string, path string, q
 	if contentMD5 != "" {
 		httpReq.Header.Set("Content-MD5", contentMD5)
 	}
+
+	if (client.secureTransport == "false" || client.secureTransport == "true") && client.sourceIp != "" {
+		httpReq.Header["x-acs-source-ip"] = []string{client.sourceIp}
+		httpReq.Header["x-acs-secure-transport"] = []string{client.secureTransport}
+	}
+
 	// TODO move to util and add build val flag
 	httpReq.Header.Set("Date", util.GetGMTime())
 	httpReq.Header.Set("Accept", "application/json")
@@ -173,13 +204,13 @@ func (client *Client) Invoke(region common.Region, method string, path string, q
 
 	if client.debug {
 		var prettyJSON bytes.Buffer
-		err = json.Indent(&prettyJSON, body, "", "    ")
-		log.Println(string(prettyJSON.Bytes()))
+		_ = json.Indent(&prettyJSON, body, "", "    ")
+		log.Println(prettyJSON.String())
 	}
 
 	if statusCode >= 400 && statusCode <= 599 {
 		errorResponse := common.ErrorResponse{}
-		err = json.Unmarshal(body, &errorResponse)
+		_ = json.Unmarshal(body, &errorResponse)
 		ecsError := &common.Error{
 			ErrorResponse: errorResponse,
 			StatusCode:    statusCode,
@@ -187,7 +218,7 @@ func (client *Client) Invoke(region common.Region, method string, path string, q
 		return ecsError
 	}
 
-	if response != nil {
+	if response != nil && len(body) > 0 {
 		err = json.Unmarshal(body, response)
 		//log.Printf("%++v", response)
 		if err != nil {
diff --git a/cs/clusters.go b/cs/clusters.go
index fd81770..1dee1c5 100644
--- a/cs/clusters.go
+++ b/cs/clusters.go
@@ -11,6 +11,7 @@ import (
 	"fmt"
 
 	"encoding/json"
+
 	"github.com/denverdino/aliyungo/common"
 	"github.com/denverdino/aliyungo/ecs"
 )
@@ -27,8 +28,19 @@ const (
 	DeleteFailed = ClusterState("deleteFailed")
 	Deleted      = ClusterState("deleted")
 	InActive     = ClusterState("inactive")
+
+	ClusterTypeKubernetes        = "Kubernetes"
+	ClusterTypeManagedKubernetes = "ManagedKubernetes"
+
+	ClusterTypeServerlessKubernetes = "Ask"
+
+	ProfileServerlessKubernetes = "Serverless"
 )
 
+var NodeStableClusterState = []ClusterState{Running, Updating, Failed, DeleteFailed, Deleted, InActive}
+
+var NodeUnstableClusterState = []ClusterState{Initial, Scaling, Deleting}
+
 type NodeStatus struct {
 	Health   int64 `json:"health"`
 	Unhealth int64 `json:"unhealth"`
@@ -60,6 +72,7 @@ type ClusterType struct {
 	NodeStatus             string          `json:"node_status"`
 	DockerVersion          string          `json:"docker_version"`
 	ClusterType            string          `json:"cluster_type"`
+	Profile                string          `json:"profile"`
 }
 
 func (client *Client) DescribeClusters(nameFilter string) (clusters []ClusterType, err error) {
@@ -92,14 +105,19 @@ type ClusterCreationArgs struct {
 	ECSImageID       string           `json:"ecs_image_id,omitempty"`
 	IOOptimized      ecs.IoOptimized  `json:"io_optimized"`
 	ReleaseEipFlag   bool             `json:"release_eip_flag"`
+	NeedSLB          bool             `json:"need_slb"`
 }
 
-type ClusterCreationResponse struct {
+type ClusterCommonResponse struct {
 	Response
-	ClusterID string `json:"cluster_id"`
+	ClusterID  string `json:"cluster_id"`
+	Token      string `json:"token,omitempty"`
+	TaskId     string `json:"task_id,omitempty"`
+	InstanceId string `json:"instanceId"`
 }
 
-func (client *Client) CreateCluster(region common.Region, args *ClusterCreationArgs) (cluster ClusterCreationResponse, err error) {
+//Deprecated
+func (client *Client) CreateCluster(region common.Region, args *ClusterCreationArgs) (cluster ClusterCommonResponse, err error) {
 	err = client.Invoke(region, http.MethodPost, "/clusters", nil, args, &cluster)
 	return
 }
@@ -127,30 +145,55 @@ type KubernetesStackArgs struct {
 	SNatEntry                bool             `json:"SNatEntry,omitempty"`
 }
 
+// Deprecated
 type KubernetesCreationArgs struct {
-	DisableRollback          bool             `json:"disable_rollback"`
-	Name                     string           `json:"name"`
-	TimeoutMins              int64            `json:"timeout_mins"`
-	ZoneId                   string           `json:"zoneid,omitempty"`
-	VPCID                    string           `json:"vpcid,omitempty"`
-	VSwitchId                string           `json:"vswitchid,omitempty"`
-	ContainerCIDR            string           `json:"container_cidr,omitempty"`
-	ServiceCIDR              string           `json:"service_cidr,omitempty"`
+	DisableRollback bool     `json:"disable_rollback"`
+	Name            string   `json:"name"`
+	TimeoutMins     int64    `json:"timeout_mins"`
+	ZoneId          string   `json:"zoneid,omitempty"`
+	VPCID           string   `json:"vpcid,omitempty"`
+	RegionId        string   `json:"region_id,omitempty"`
+	VSwitchId       string   `json:"vswitchid,omitempty"`
+	VSwitchIds      []string `json:"vswitch_ids,omitempty"`
+	ImageId         string   `json:"image_id"`
+	ContainerCIDR   string   `json:"container_cidr,omitempty"`
+	ServiceCIDR     string   `json:"service_cidr,omitempty"`
+
 	MasterInstanceType       string           `json:"master_instance_type,omitempty"`
 	MasterSystemDiskSize     int64            `json:"master_system_disk_size,omitempty"`
 	MasterSystemDiskCategory ecs.DiskCategory `json:"master_system_disk_category,omitempty"`
+
+	MasterInstanceChargeType string `json:"master_instance_charge_type"`
+	MasterPeriodUnit         string `json:"master_period_unit"`
+	MasterPeriod             int    `json:"master_period"`
+	MasterAutoRenew          bool   `json:"master_auto_renew"`
+	MasterAutoRenewPeriod    int    `json:"master_auto_renew_period"`
+
 	WorkerInstanceType       string           `json:"worker_instance_type,omitempty"`
+	WorkerInstanceTypes      []string         `json:"worker_instance_types,omitempty"`
 	WorkerSystemDiskSize     int64            `json:"worker_system_disk_size,omitempty"`
 	WorkerSystemDiskCategory ecs.DiskCategory `json:"worker_system_disk_category,omitempty"`
-	WorkerDataDisk           bool             `json:"worker_data_disk,omitempty"`
-	WorkerDataDiskCategory   string           `json:"worker_data_disk_category,omitempty"`
+	WorkerDataDisk           bool             `json:"worker_data_disk"`
+	WorkerDataDiskCategory   ecs.DiskCategory `json:"worker_data_disk_category,omitempty"`
 	WorkerDataDiskSize       int64            `json:"worker_data_disk_size,omitempty"`
-	LoginPassword            string           `json:"login_password,omitempty"`
-	KeyPair                  string           `json:"key_pair,omitempty"`
-	NumOfNodes               int64            `json:"num_of_nodes,omitempty"`
-	SNatEntry                bool             `json:"snat_entry,omitempty"`
-	SSHFlags                 bool             `json:"ssh_flags,omitempty"`
-	CloudMonitorFlags        bool             `json:"cloud_monitor_flags,omitempty"`
+
+	WorkerInstanceChargeType string `json:"worker_instance_charge_type"`
+	WorkerPeriodUnit         string `json:"worker_period_unit"`
+	WorkerPeriod             int    `json:"worker_period"`
+	WorkerAutoRenew          bool   `json:"worker_auto_renew"`
+	WorkerAutoRenewPeriod    int    `json:"worker_auto_renew_period"`
+
+	LoginPassword     string `json:"login_password,omitempty"`
+	KeyPair           string `json:"key_pair,omitempty"`
+	UserCA            string `json:"user_ca,omitempty"`
+	NumOfNodes        int64  `json:"num_of_nodes,omitempty"`
+	SNatEntry         bool   `json:"snat_entry"`
+	SSHFlags          bool   `json:"ssh_flags"`
+	CloudMonitorFlags bool   `json:"cloud_monitor_flags"`
+	NodeCIDRMask      string `json:"node_cidr_mask,omitempty"`
+	LoggingType       string `json:"logging_type,omitempty"`
+	SLSProjectName    string `json:"sls_project_name,omitempty"`
+	PublicSLB         bool   `json:"public_slb"`
 
 	ClusterType string `json:"cluster_type"`
 	Network     string `json:"network,omitempty"`
@@ -159,49 +202,73 @@ type KubernetesCreationArgs struct {
 	StackParams       KubernetesStackArgs `json:"stack_params,omitempty"`
 }
 
+// Deprecated
 type KubernetesMultiAZCreationArgs struct {
-	DisableRollback          bool             `json:"disable_rollback"`
-	Name                     string           `json:"name"`
-	TimeoutMins              int64            `json:"timeout_mins"`
-	ClusterType              string           `json:"cluster_type"`
-	MultiAZ                  bool             `json:"multi_az,omitempty"`
-	VPCID                    string           `json:"vpcid,omitempty"`
-	ContainerCIDR            string           `json:"container_cidr"`
-	ServiceCIDR              string           `json:"service_cidr"`
-	VSwitchIdA               string           `json:"vswitch_id_a,omitempty"`
-	VSwitchIdB               string           `json:"vswitch_id_b,omitempty"`
-	VSwitchIdC               string           `json:"vswitch_id_c,omitempty"`
+	DisableRollback bool   `json:"disable_rollback"`
+	Name            string `json:"name"`
+	TimeoutMins     int64  `json:"timeout_mins"`
+	ClusterType     string `json:"cluster_type"`
+	MultiAZ         bool   `json:"multi_az"`
+	VPCID           string `json:"vpcid,omitempty"`
+	ImageId         string `json:"image_id"`
+	ContainerCIDR   string `json:"container_cidr"`
+	ServiceCIDR     string `json:"service_cidr"`
+	VSwitchIdA      string `json:"vswitch_id_a,omitempty"`
+	VSwitchIdB      string `json:"vswitch_id_b,omitempty"`
+	VSwitchIdC      string `json:"vswitch_id_c,omitempty"`
+
 	MasterInstanceTypeA      string           `json:"master_instance_type_a,omitempty"`
 	MasterInstanceTypeB      string           `json:"master_instance_type_b,omitempty"`
 	MasterInstanceTypeC      string           `json:"master_instance_type_c,omitempty"`
 	MasterSystemDiskCategory ecs.DiskCategory `json:"master_system_disk_category"`
 	MasterSystemDiskSize     int64            `json:"master_system_disk_size"`
+
+	MasterInstanceChargeType string `json:"master_instance_charge_type"`
+	MasterPeriodUnit         string `json:"master_period_unit"`
+	MasterPeriod             int    `json:"master_period"`
+	MasterAutoRenew          bool   `json:"master_auto_renew"`
+	MasterAutoRenewPeriod    int    `json:"master_auto_renew_period"`
+
 	WorkerInstanceTypeA      string           `json:"worker_instance_type_a,omitempty"`
 	WorkerInstanceTypeB      string           `json:"worker_instance_type_b,omitempty"`
 	WorkerInstanceTypeC      string           `json:"worker_instance_type_c,omitempty"`
 	WorkerSystemDiskCategory ecs.DiskCategory `json:"worker_system_disk_category"`
 	WorkerSystemDiskSize     int64            `json:"worker_system_disk_size"`
 	WorkerDataDisk           bool             `json:"worker_data_disk"`
-	WorkerDataDiskCategory   string           `json:"worker_data_disk_category"`
+	WorkerDataDiskCategory   ecs.DiskCategory `json:"worker_data_disk_category"`
 	WorkerDataDiskSize       int64            `json:"worker_data_disk_size"`
-	NumOfNodesA              int64            `json:"num_of_nodes_a"`
-	NumOfNodesB              int64            `json:"num_of_nodes_b"`
-	NumOfNodesC              int64            `json:"num_of_nodes_c"`
-	LoginPassword            string           `json:"login_password,omitempty"`
-	KeyPair                  string           `json:"key_pair,omitempty"`
-	SSHFlags                 bool             `json:"ssh_flags"`
-	CloudMonitorFlags        bool             `json:"cloud_monitor_flags"`
+
+	WorkerInstanceChargeType string `json:"worker_instance_charge_type"`
+	WorkerPeriodUnit         string `json:"worker_period_unit"`
+	WorkerPeriod             int    `json:"worker_period"`
+	WorkerAutoRenew          bool   `json:"worker_auto_renew"`
+	WorkerAutoRenewPeriod    int    `json:"worker_auto_renew_period"`
+
+	NumOfNodesA       int64  `json:"num_of_nodes_a"`
+	NumOfNodesB       int64  `json:"num_of_nodes_b"`
+	NumOfNodesC       int64  `json:"num_of_nodes_c"`
+	LoginPassword     string `json:"login_password,omitempty"`
+	KeyPair           string `json:"key_pair,omitempty"`
+	UserCA            string `json:"user_ca,omitempty"`
+	SSHFlags          bool   `json:"ssh_flags"`
+	CloudMonitorFlags bool   `json:"cloud_monitor_flags"`
+	NodeCIDRMask      string `json:"node_cidr_mask,omitempty"`
+	LoggingType       string `json:"logging_type,omitempty"`
+	SLSProjectName    string `json:"sls_project_name,omitempty"`
+	PublicSLB         bool   `json:"public_slb"`
 
 	KubernetesVersion string `json:"kubernetes_version,omitempty"`
 	Network           string `json:"network,omitempty"`
 }
 
-func (client *Client) CreateKubernetesMultiAZCluster(region common.Region, args *KubernetesMultiAZCreationArgs) (cluster ClusterCreationResponse, err error) {
+// Deprecated
+func (client *Client) CreateKubernetesMultiAZCluster(region common.Region, args *KubernetesMultiAZCreationArgs) (cluster ClusterCommonResponse, err error) {
 	err = client.Invoke(region, http.MethodPost, "/clusters", nil, args, &cluster)
 	return
 }
 
-func (client *Client) CreateKubernetesCluster(region common.Region, args *KubernetesCreationArgs) (cluster ClusterCreationResponse, err error) {
+// Deprecated
+func (client *Client) CreateKubernetesCluster(region common.Region, args *KubernetesCreationArgs) (cluster ClusterCommonResponse, err error) {
 	err = client.Invoke(region, http.MethodPost, "/clusters", nil, args, &cluster)
 	return
 }
@@ -214,19 +281,49 @@ type KubernetesClusterMetaData struct {
 	SubClass          string `json:"SubClass"`
 }
 
+// deprecated
 type KubernetesClusterParameter struct {
-	ServiceCidr              string `json:"ServiceCIDR"`
-	ContainerCidr            string `json:"ContainerCIDR"`
-	DockerVersion            string `json:"DockerVersion"`
-	EtcdVersion              string `json:"EtcdVersion"`
-	KubernetesVersion        string `json:"KubernetesVersion"`
-	VPCID                    string `json:"VpcId"`
-	KeyPair                  string `json:"KeyPair"`
-	MasterSystemDiskCategory string `json:"MasterSystemDiskCategory"`
-	MasterSystemDiskSize     string `json:"MasterSystemDiskSize"`
-	WorkerSystemDiskCategory string `json:"WorkerSystemDiskCategory"`
-	WorkerSystemDiskSize     string `json:"WorkerSystemDiskSize"`
-	ZoneId                   string `json:"ZoneId"`
+	ServiceCidr       string `json:"ServiceCIDR"`
+	ContainerCidr     string `json:"ContainerCIDR"`
+	DockerVersion     string `json:"DockerVersion"`
+	EtcdVersion       string `json:"EtcdVersion"`
+	KubernetesVersion string `json:"KubernetesVersion"`
+	VPCID             string `json:"VpcId"`
+	ImageId           string `json:"ImageId"`
+	KeyPair           string `json:"KeyPair"`
+
+	MasterSystemDiskCategory ecs.DiskCategory `json:"MasterSystemDiskCategory"`
+	MasterSystemDiskSize     string           `json:"MasterSystemDiskSize"`
+	MasterImageId            string           `json:"MasterImageId"`
+
+	MasterInstanceChargeType string `json:"MasterInstanceChargeType"`
+	MasterPeriodUnit         string `json:"MasterPeriodUnit"`
+	MasterPeriod             string `json:"MasterPeriod"`
+	MasterAutoRenew          *bool
+	RawMasterAutoRenew       string `json:"MasterAutoRenew"`
+	MasterAutoRenewPeriod    string `json:"MasterAutoRenewPeriod"`
+
+	WorkerSystemDiskCategory ecs.DiskCategory `json:"WorkerSystemDiskCategory"`
+	WorkerSystemDiskSize     string           `json:"WorkerSystemDiskSize"`
+	WorkerImageId            string           `json:"WorkerImageId"`
+	WorkerDataDisk           *bool
+	RawWorkerDataDisk        string           `json:"WorkerDataDisk"`
+	WorkerDataDiskCategory   ecs.DiskCategory `json:"WorkerDataDiskCategory"`
+	WorkerDataDiskSize       string           `json:"WorkerDataDiskSize"`
+
+	WorkerInstanceChargeType string `json:"WorkerInstanceChargeType"`
+	WorkerPeriodUnit         string `json:"WorkerPeriodUnit"`
+	WorkerPeriod             string `json:"WorkerPeriod"`
+	WorkerAutoRenew          *bool
+	RawWorkerAutoRenew       string `json:"WorkerAutoRenew"`
+	WorkerAutoRenewPeriod    string `json:"WorkerAutoRenewPeriod"`
+
+	ZoneId         string `json:"ZoneId"`
+	NodeCIDRMask   string `json:"NodeCIDRMask"`
+	LoggingType    string `json:"LoggingType"`
+	SLSProjectName string `json:"SLSProjectName"`
+	PublicSLB      *bool
+	RawPublicSLB   string `json:"PublicSLB"`
 
 	// Single AZ
 	MasterInstanceType string `json:"MasterInstanceType"`
@@ -249,6 +346,7 @@ type KubernetesClusterParameter struct {
 	VSwitchIdC          string `json:"VSwitchIdC"`
 }
 
+// Deprecated
 type KubernetesCluster struct {
 	ClusterType
 
@@ -278,18 +376,25 @@ type KubernetesCluster struct {
 	Parameters KubernetesClusterParameter `json:"parameters"`
 }
 
+// Deprecated
 func (client *Client) DescribeKubernetesCluster(id string) (cluster KubernetesCluster, err error) {
 	err = client.Invoke("", http.MethodGet, "/clusters/"+id, nil, nil, &cluster)
 	if err != nil {
 		return cluster, err
 	}
+
 	var metaData KubernetesClusterMetaData
 	err = json.Unmarshal([]byte(cluster.RawMetaData), &metaData)
+	if err != nil {
+		return cluster, err
+	}
 	cluster.MetaData = metaData
 	cluster.RawMetaData = ""
+
 	return
 }
 
+// Deprecated
 type ClusterResizeArgs struct {
 	Size             int64            `json:"size"`
 	InstanceType     string           `json:"instance_type"`
@@ -304,24 +409,27 @@ type ModifyClusterNameArgs struct {
 	Name string `json:"name"`
 }
 
+// Deprecated
 func (client *Client) ResizeCluster(clusterID string, args *ClusterResizeArgs) error {
 	return client.Invoke("", http.MethodPut, "/clusters/"+clusterID, nil, args, nil)
 }
 
 // deprecated
-// use ResizeKubernetesCluster instead
+// use ScaleKubernetesCluster instead
 func (client *Client) ResizeKubernetes(clusterID string, args *KubernetesCreationArgs) error {
 	return client.Invoke("", http.MethodPut, "/clusters/"+clusterID, nil, args, nil)
 }
 
+// Deprecated
 type KubernetesClusterResizeArgs struct {
 	DisableRollback bool   `json:"disable_rollback"`
 	TimeoutMins     int64  `json:"timeout_mins"`
 	LoginPassword   string `json:"login_password,omitempty"`
 
 	// Single AZ
-	WorkerInstanceType string `json:"worker_instance_type"`
-	NumOfNodes         int64  `json:"num_of_nodes"`
+	WorkerInstanceType  string   `json:"worker_instance_type"`
+	WorkerInstanceTypes []string `json:"worker_instance_types"`
+	NumOfNodes          int64    `json:"num_of_nodes"`
 
 	// Multi AZ
 	WorkerInstanceTypeA string `json:"worker_instance_type_a"`
@@ -332,14 +440,45 @@ type KubernetesClusterResizeArgs struct {
 	NumOfNodesC         int64  `json:"num_of_nodes_c"`
 }
 
+// deprecated
+// use ScaleKubernetesCluster instead
 func (client *Client) ResizeKubernetesCluster(clusterID string, args *KubernetesClusterResizeArgs) error {
 	return client.Invoke("", http.MethodPut, "/clusters/"+clusterID, nil, args, nil)
 }
 
+// Deprecated
+type KubernetesClusterScaleArgs struct {
+	LoginPassword            string           `json:"login_password,omitempty"`
+	KeyPair                  string           `json:"key_pair,omitempty"`
+	WorkerInstanceTypes      []string         `json:"worker_instance_types"`
+	WorkerSystemDiskSize     int64            `json:"worker_system_disk_size"`
+	WorkerSystemDiskCategory ecs.DiskCategory `json:"worker_system_disk_category"`
+	WorkerDataDisk           bool             `json:"worker_data_disk"`
+	Count                    int              `json:"count"`
+
+	// Edge worker related args
+	IsEdgeWorker          bool   `json:"is_edge_worker"`
+	EnsRegionId           string `json:"ens_region_id"`
+	EnsInternetChargeType string `json:"ens_internet_charge_type"`
+
+	//data disk
+	WorkerDataDiskCategory  ecs.DiskCategory `json:"worker_data_disk_category"`
+	WorkerDataDiskSize      int64            `json:"worker_data_disk_size"`
+	WorkerDataDiskEncrypted string           `json:"worker_data_disk_encrypted"`
+	WorkerDataDiskKMSKeyId  string           `json:"worker_data_disk_kms_key_id"`
+}
+
+// Deprecated
+func (client *Client) ScaleKubernetesCluster(clusterID string, args *KubernetesClusterScaleArgs) error {
+	return client.Invoke("", http.MethodPost, "/api/v2/clusters/"+clusterID, nil, args, nil)
+}
+
+// Deprecated
 func (client *Client) ModifyClusterName(clusterID, clusterName string) error {
 	return client.Invoke("", http.MethodPost, "/clusters/"+clusterID+"/name/"+clusterName, nil, nil, nil)
 }
 
+// Deprecated
 func (client *Client) DeleteCluster(clusterID string) error {
 	return client.Invoke("", http.MethodDelete, "/clusters/"+clusterID, nil, nil, nil)
 }
@@ -355,10 +494,25 @@ func (client *Client) GetClusterCerts(id string) (certs ClusterCerts, err error)
 	return
 }
 
+type ClusterEndpoints struct {
+	ApiServerEndpoint         string `json:"api_server_endpoint"`
+	DashboardEndpoint         string `json:"dashboard_endpoint"`
+	MiranaEndpoint            string `json:"mirana_endpoint"`
+	ReverseTunnelEndpoint     string `json:"reverse_tunnel_endpoint"`
+	IntranetApiServerEndpoint string `json:"intranet_api_server_endpoint"`
+}
+
+func (client *Client) GetClusterEndpoints(id string) (clusterEndpoints ClusterEndpoints, err error) {
+	err = client.Invoke("", http.MethodGet, "/clusters/"+id+"/endpoints", nil, nil, &clusterEndpoints)
+	return
+}
+
 type ClusterConfig struct {
 	Config string `json:"config"`
 }
 
+// deprecated
+// Please use new api DescribeClusterUserConfig
 func (client *Client) GetClusterConfig(id string) (config ClusterConfig, err error) {
 	err = client.Invoke("", http.MethodGet, "/k8s/"+id+"/user_config", nil, nil, &config)
 	return
@@ -375,6 +529,7 @@ type KubernetesNodeType struct {
 	HostName           string   `json:"host_name"`
 	ImageId            string   `json:"image_id"`
 	InstanceId         string   `json:"instance_id"`
+	NodeName           string   `json:"node_name"`
 }
 
 type GetKubernetesClusterNodesResponse struct {
@@ -383,9 +538,14 @@ type GetKubernetesClusterNodesResponse struct {
 	Nodes []KubernetesNodeType `json:"nodes"`
 }
 
-func (client *Client) GetKubernetesClusterNodes(id string, pagination common.Pagination) (nodes []KubernetesNodeType, paginationResult *PaginationResult, err error) {
+// GetKubernetesClusterNodes is used to get cluster nodes or node pool nodes
+func (client *Client) GetKubernetesClusterNodes(id string, pagination common.Pagination, nodepoolId string) (nodes []KubernetesNodeType, paginationResult *PaginationResult, err error) {
 	response := &GetKubernetesClusterNodesResponse{}
-	err = client.Invoke("", http.MethodGet, "/clusters/"+id+"/nodes?pageNumber="+strconv.Itoa(pagination.PageNumber)+"&pageSize="+strconv.Itoa(pagination.PageSize), nil, nil, &response)
+	if nodepoolId != "" {
+		err = client.Invoke("", http.MethodGet, "/clusters/"+id+"/nodes?nodepool_id="+nodepoolId+"&pageNumber="+strconv.Itoa(pagination.PageNumber)+"&pageSize="+strconv.Itoa(pagination.PageSize), nil, nil, &response)
+	} else {
+		err = client.Invoke("", http.MethodGet, "/clusters/"+id+"/nodes?pageNumber="+strconv.Itoa(pagination.PageNumber)+"&pageSize="+strconv.Itoa(pagination.PageSize), nil, nil, &response)
+	}
 	if err != nil {
 		return nil, nil, err
 	}
@@ -395,7 +555,8 @@ func (client *Client) GetKubernetesClusterNodes(id string, pagination common.Pag
 
 const ClusterDefaultTimeout = 300
 const DefaultWaitForInterval = 10
-const DefaultPreSleepTime = 240
+const DefaultPreCheckSleepTime = 20
+const DefaultPreSleepTime = 220
 
 // WaitForCluster waits for instance to given status
 // when instance.NotFound wait until timeout
@@ -403,15 +564,23 @@ func (client *Client) WaitForClusterAsyn(clusterId string, status ClusterState,
 	if timeout <= 0 {
 		timeout = ClusterDefaultTimeout
 	}
+
+	// Sleep 20 second to check cluster creating or failed
+	sleep := math.Min(float64(timeout), float64(DefaultPreCheckSleepTime))
+	time.Sleep(time.Duration(sleep) * time.Second)
+
 	cluster, err := client.DescribeCluster(clusterId)
 	if err != nil {
 		return err
+	} else if cluster.State == Failed {
+		return fmt.Errorf("Waitting for cluster %s %s failed. Looking the specified reason in the web console.", clusterId, status)
 	} else if cluster.State == status {
 		//TODO
 		return nil
 	}
+
 	// Create or Reset cluster usually cost at least 4 min, so there will sleep a long time before polling
-	sleep := math.Min(float64(timeout), float64(DefaultPreSleepTime))
+	sleep = math.Min(float64(timeout), float64(DefaultPreSleepTime))
 	time.Sleep(time.Duration(sleep) * time.Second)
 
 	for {
diff --git a/cs/clusters_test.go b/cs/clusters_test.go
index 2524118..b44b449 100644
--- a/cs/clusters_test.go
+++ b/cs/clusters_test.go
@@ -42,7 +42,7 @@ func TestClient_DescribeKubernetesClusters(t *testing.T) {
 	}
 
 	for _, cluster := range clusters {
-		if cluster.ClusterType != "Kubernetes" {
+		if cluster.ClusterType != "Kubernetes" && cluster.ClusterType != "ManagedKubernetes" {
 			continue
 		}
 		t.Logf("Cluster: %++v", cluster)
@@ -51,24 +51,15 @@ func TestClient_DescribeKubernetesClusters(t *testing.T) {
 			t.Errorf("Failed to DescribeCluster: %v", err)
 		}
 		t.Logf("Cluster Describe: %++v", c)
-		t.Logf("Cluster KeyPair %v", c.Parameters.KeyPair)
 
 		if c.MetaData.MultiAZ || c.MetaData.SubClass == "3az" {
 			t.Logf("%v is a MultiAZ kubernetes cluster", c.ClusterID)
-			t.Logf("Cluster VSWA ID %v", c.Parameters.VSwitchIdA)
-			t.Logf("Cluster VSWB ID %v", c.Parameters.VSwitchIdB)
-			t.Logf("Cluster VSWC ID %v", c.Parameters.VSwitchIdC)
-			t.Logf("Cluster MasterInstanceTypeA %v", c.Parameters.MasterInstanceTypeA)
-			t.Logf("Cluster MasterInstanceTypeB %v", c.Parameters.MasterInstanceTypeB)
-			t.Logf("Cluster MasterInstanceTypeC %v", c.Parameters.MasterInstanceTypeC)
-			t.Logf("Cluster NumOfNodeA %v", c.Parameters.NumOfNodesA)
-			t.Logf("Cluster NumOfNodeB %v", c.Parameters.NumOfNodesB)
-			t.Logf("Cluster NumOfNodeC %v", c.Parameters.NumOfNodesC)
 		} else {
-			t.Logf("%v is a single kubernetes cluster", c.ClusterID)
-			t.Logf("Cluster VSW ID %v", c.Parameters.VSwitchID)
-			t.Logf("Cluster MasterInstanceType %v", c.Parameters.MasterInstanceType)
-			t.Logf("Cluster NumOfNode %v", c.Parameters.NumOfNodes)
+			if cluster.ClusterType == "ManagedKubernetes" {
+				t.Logf("%v is a Managed kubernetes cluster", c.ClusterID)
+			} else {
+				t.Logf("%v is a SingleAZ kubernetes cluster", c.ClusterID)
+			}
 		}
 	}
 }
@@ -98,6 +89,16 @@ func TestListClusters(t *testing.T) {
 	}
 }
 
+func _TestGetClusterEndpoints(t *testing.T) {
+	client := NewTestClientForDebug()
+	clusterId := "c213c31b97430433c87afe4852b6a08ef"
+	clusterEndpoints, err := client.GetClusterEndpoints(clusterId)
+	if err != nil {
+		t.Fatalf("Failed to GetClusterEndpoints: %v", err)
+	}
+	t.Logf("Succeed getting clusterEndpoints %v", clusterEndpoints)
+}
+
 func _TestCreateClusters(t *testing.T) {
 
 	client := NewTestClientForDebug()
@@ -155,6 +156,10 @@ func _TestCreateKubernetesCluster(t *testing.T) {
 		WorkerDataDisk:           true,
 		WorkerDataDiskCategory:   "cloud_efficiency",
 		WorkerDataDiskSize:       100,
+		PublicSLB:                true,
+		NodeCIDRMask:             "25",
+		LoggingType:              "SLS",
+		SLSProjectName:           "k8s-test-my-terraform-singleaz",
 	}
 	cluster, err := client.CreateKubernetesCluster(common.Hangzhou, &args)
 	if err != nil {
@@ -164,39 +169,98 @@ func _TestCreateKubernetesCluster(t *testing.T) {
 	t.Logf("Cluster: %++v", cluster)
 }
 
-func _TestCreateKubernetesMultiAZCluster(t *testing.T) {
+func _TestCreateManagedKubernetesCluster(t *testing.T) {
 
 	client := NewTestClientForDebug()
 
-	args := KubernetesMultiAZCreationArgs{
-		Name:                     "multiaz-test1",
-		ClusterType:              "Kubernetes",
-		DisableRollback:          true,
-		MultiAZ:                  true,
-		VPCID:                    "vpc-id",
-		VSwitchIdA:               "vsw-id1",
-		VSwitchIdB:               "vsw-id2",
-		VSwitchIdC:               "vsw-id3",
-		NumOfNodesA:              1,
-		NumOfNodesB:              1,
-		NumOfNodesC:              1,
-		MasterInstanceTypeA:      "ecs.sn1ne.large",
-		MasterInstanceTypeB:      "ecs.sn1ne.large",
-		MasterInstanceTypeC:      "ecs.sn1ne.large",
+	args := KubernetesCreationArgs{
+		Name:            "single-managed-az-k8s",
+		ClusterType:     "ManagedKubernetes",
+		DisableRollback: true,
+		//VPCID:                    "vpc-id",
+		//VSwitchId:                "vsw-id",
+		ZoneId:                   "cn-hangzhou-g",
+		SNatEntry:                true,
+		NumOfNodes:               2,
+		MasterInstanceType:       "ecs.sn1ne.large",
 		MasterSystemDiskCategory: "cloud_efficiency",
 		MasterSystemDiskSize:     40,
-		WorkerInstanceTypeA:      "ecs.sn1ne.large",
-		WorkerInstanceTypeB:      "ecs.sn1ne.large",
-		WorkerInstanceTypeC:      "ecs.sn1ne.large",
+		WorkerInstanceType:       "ecs.sn1ne.large",
 		WorkerSystemDiskCategory: "cloud_efficiency",
 		WorkerSystemDiskSize:     40,
 		SSHFlags:                 true,
-		ContainerCIDR:            "172.17.0.0/16",
-		ServiceCIDR:              "172.20.0.0/20",
+		ContainerCIDR:            "172.16.0.0/16",
+		ServiceCIDR:              "172.19.0.0/20",
 		LoginPassword:            "test-password123",
 		WorkerDataDisk:           true,
 		WorkerDataDiskCategory:   "cloud_efficiency",
 		WorkerDataDiskSize:       100,
+		PublicSLB:                true,
+		NodeCIDRMask:             "25",
+		LoggingType:              "SLS",
+		SLSProjectName:           "k8s-test-my-terraform-singleaz",
+	}
+	cluster, err := client.CreateKubernetesCluster(common.Hangzhou, &args)
+	if err != nil {
+		t.Fatalf("Failed to CreateKubernetesCluster: %v", err)
+	}
+
+	t.Logf("Cluster: %++v", cluster)
+}
+
+func _TestCreateKubernetesMultiAZCluster(t *testing.T) {
+
+	client := NewTestClientForDebug()
+
+	args := KubernetesMultiAZCreationArgs{
+		Name:            "multiaz-test",
+		ClusterType:     "Kubernetes",
+		DisableRollback: true,
+		MultiAZ:         true,
+		VPCID:           "vpc-id",
+		VSwitchIdA:      "vsw-id1",
+		VSwitchIdB:      "vsw-id2",
+		VSwitchIdC:      "vsw-id3",
+		NumOfNodesA:     1,
+		NumOfNodesB:     1,
+		NumOfNodesC:     1,
+
+		MasterInstanceTypeA:      "ecs.ic5.xlarge",
+		MasterInstanceTypeB:      "ecs.ic5.xlarge",
+		MasterInstanceTypeC:      "ecs.ic5.xlarge",
+		MasterSystemDiskCategory: "cloud_efficiency",
+		MasterSystemDiskSize:     40,
+
+		MasterInstanceChargeType: "PrePaid",
+		MasterPeriodUnit:         "Week",
+		MasterPeriod:             1,
+		MasterAutoRenew:          true,
+		MasterAutoRenewPeriod:    1,
+
+		WorkerInstanceTypeA:      "ecs.ic5.xlarge",
+		WorkerInstanceTypeB:      "ecs.ic5.xlarge",
+		WorkerInstanceTypeC:      "ecs.ic5.xlarge",
+		WorkerSystemDiskCategory: "cloud_efficiency",
+		WorkerSystemDiskSize:     40,
+
+		WorkerInstanceChargeType: "PrePaid",
+		WorkerPeriodUnit:         "Week",
+		WorkerPeriod:             1,
+		WorkerAutoRenew:          true,
+		WorkerAutoRenewPeriod:    1,
+
+		SSHFlags:               true,
+		ContainerCIDR:          "172.20.0.0/16",
+		ServiceCIDR:            "172.21.0.0/20",
+		LoginPassword:          "test-password123",
+		ImageId:                "centos_7_03_64_20G_alibase_20170818.vhd",
+		KubernetesVersion:      "1.11.2",
+		WorkerDataDisk:         true,
+		WorkerDataDiskCategory: "cloud_efficiency",
+		WorkerDataDiskSize:     100,
+		NodeCIDRMask:           "25",
+		LoggingType:            "SLS",
+		SLSProjectName:         "k8s-test-my-terraform",
 	}
 	cluster, err := client.CreateKubernetesMultiAZCluster(common.Hangzhou, &args)
 	if err != nil {
@@ -206,19 +270,36 @@ func _TestCreateKubernetesMultiAZCluster(t *testing.T) {
 	t.Logf("Cluster: %++v", cluster)
 }
 
-func _TestResizeKubernetesCluster(t *testing.T) {
+func TestScaleKubernetesCluster(t *testing.T) {
+	// Comment below to test
+	//t.SkipNow()
+
 	client := NewTestClientForDebug()
 
-	args := KubernetesClusterResizeArgs{
-		DisableRollback:    true,
-		TimeoutMins:        60,
-		LoginPassword:      "test-password123",
-		WorkerInstanceType: "ecs.sn1ne.large",
-		NumOfNodes:         2,
+	args := KubernetesClusterScaleArgs{
+		LoginPassword:            "test-password123",
+		WorkerInstanceTypes:      []string{"ecs.sn1ne.large"},
+		WorkerSystemDiskCategory: "cloud_ssd",
+		WorkerSystemDiskSize:     int64(40),
+		Count:                    2,
+		WorkerDataDisk:           true,
+		WorkerDataDiskCategory:   "cloud_ssd",
+		WorkerDataDiskSize:       int64(200),
+	}
+
+	err := client.ScaleKubernetesCluster(TestClusterId, &args)
+	if err != nil {
+		t.Fatalf("Failed to TestScaleKubernetesCluster: %v", err)
 	}
+}
+
+func Test_GetKubernetesClusterNodes(t *testing.T) {
+	client := NewTestClientForDebug()
 
-	err := client.ResizeKubernetesCluster("c3419330390a94906bcacc55bfa9dd21f", &args)
+	resp, _, err := client.GetKubernetesClusterNodes(TestClusterId, common.Pagination{PageNumber: 1, PageSize: 1000}, NodePoolId)
 	if err != nil {
-		t.Fatalf("Failed to TestResizeKubernetesCluster: %v", err)
+		t.Errorf("Error %++v", err)
+	} else {
+		t.Logf("response: %++v", resp)
 	}
 }
diff --git a/cs/config_test.go b/cs/config_test.go
index a0e38c3..7708c75 100644
--- a/cs/config_test.go
+++ b/cs/config_test.go
@@ -2,6 +2,7 @@ package cs
 
 import (
 	"os"
+	"strconv"
 
 	"github.com/denverdino/aliyungo/common"
 )
@@ -9,10 +10,17 @@ import (
 //Modify with your Access Key Id and Access Key Secret
 
 var (
-	TestAccessKeyId     = os.Getenv("AccessKeyId")
-	TestAccessKeySecret = os.Getenv("AccessKeySecret")
-	TestSecurityToken   = os.Getenv("SecurityToken")
-	TestRegionID        = common.Region(os.Getenv("RegionId"))
+	TestAccessKeyId         = os.Getenv("AccessKeyId")
+	TestAccessKeySecret     = os.Getenv("AccessKeySecret")
+	TestSecurityToken       = os.Getenv("SecurityToken")
+	TestRegionID            = common.Region(os.Getenv("RegionId"))
+	TestVpcId               = os.Getenv("VpcId")
+	TestVSwitchId           = os.Getenv("VSwitchId")
+	TestServiceCIDR         = os.Getenv("ServiceCIDR")
+	TestClusterId           = os.Getenv("ClusterId")
+	TestPrivateIpAddress, _ = strconv.ParseBool(os.Getenv("PrivateIpAddress"))
+	TestToken               = os.Getenv("Token")
+	TestLoggingType         = os.Getenv("LoggingType")
 )
 
 var testClient *Client
diff --git a/cs/kubernetes_cluster_test.go b/cs/kubernetes_cluster_test.go
new file mode 100644
index 0000000..a349c92
--- /dev/null
+++ b/cs/kubernetes_cluster_test.go
@@ -0,0 +1,158 @@
+package cs
+
+import (
+	"testing"
+)
+
+func Test_ModifyCluster(t *testing.T) {
+	client := NewTestClientForDebug()
+
+	args := &ModifyClusterArgs{
+		DeletionProtection: false,
+		MaintenanceWindow: MaintenanceWindow{
+			Enable:          true,
+			MaintenanceTime: "2020-12-02T13:06:50.224Z",
+			Duration:        "3h",
+			WeeklyPeriod:    "Wednesday",
+		},
+	}
+
+	err := client.ModifyCluster(TestClusterId, args)
+	if err != nil {
+		t.Errorf("Error %++v", err)
+	} else {
+		t.Logf("OK")
+	}
+}
+
+func Test_UpgradeCluster(t *testing.T) {
+	client := NewTestClientForDebug()
+
+	args := &UpgradeClusterArgs{
+		Version: "1.14.8-aliyun.1",
+	}
+
+	err := client.UpgradeCluster(TestClusterId, args)
+	if err != nil {
+		t.Errorf("Error %++v", err)
+	} else {
+		t.Logf("OK")
+	}
+}
+
+func Test_CancelUpgradeCluster(t *testing.T) {
+	client := NewTestClientForDebug()
+
+	err := client.CancelUpgradeCluster(TestClusterId)
+	if err != nil {
+		t.Errorf("Error %++v", err)
+	} else {
+		t.Logf("OK")
+	}
+}
+
+func Test_QueryUpgradeClusterResult(t *testing.T) {
+	client := NewTestClientForDebug()
+
+	result, err := client.QueryUpgradeClusterResult(TestClusterId)
+	if err != nil {
+		t.Errorf("Error %++v", err)
+	} else {
+		t.Logf("OK, result: %++v", result)
+	}
+}
+
+func Test_CreateDelicatedKubernetesCluster(t *testing.T) {
+	t.SkipNow()
+	client := NewTestClientForDebug()
+
+	request := &DelicatedKubernetesClusterCreationRequest{}
+	response, err := client.CreateDelicatedKubernetesCluster(request)
+	if err != nil {
+		t.Fatalf("Error %++v", err)
+	} else {
+		t.Logf("Response %++v", response)
+	}
+}
+
+func Test_CreateManagedKubernetesCluster(t *testing.T) {
+	t.SkipNow()
+	client := NewTestClientForDebug()
+
+	request := &ManagedKubernetesClusterCreationRequest{}
+
+	response, err := client.CreateManagedKubernetesCluster(request)
+	if err != nil {
+		t.Fatalf("Error %++v", err)
+	} else {
+		t.Logf("Response %++v", response)
+	}
+}
+
+func Test_DescribeKubernetesClusterDetail(t *testing.T) {
+	client := NewTestClientForDebug()
+
+	cluster, err := client.DescribeKubernetesClusterDetail(TestClusterId)
+
+	if err != nil {
+		t.Fatalf("Error %++v", err)
+	} else {
+		t.Logf("Response = %++v", cluster)
+		t.Logf("MetaData = %++v", cluster.GetMetaData())
+	}
+}
+
+func Test_ScaleOutKubernetesCluster(t *testing.T) {
+	client := NewTestClientForDebug()
+
+	request := &ScaleOutKubernetesClusterRequest{
+		LoginPassword:            "Hello1234",
+		WorkerVSwitchIds:         []string{"vsw-xxxx"},
+		WorkerInstanceTypes:      []string{"ecs.n4.xlarge"},
+		WorkerInstanceChargeType: "PostPaid",
+		WorkerPeriod:             1,
+		WorkerPeriodUnit:         "1",
+		WorkerAutoRenew:          true,
+		WorkerAutoRenewPeriod:    1,
+		WorkerDataDisk:           true,
+		WorkerDataDisks: []DataDisk{
+			{
+				Category: "cloud_ssd",
+				Size:     "200",
+			},
+			{
+				Category: "cloud_ssd",
+				Size:     "300",
+			},
+		},
+		Tags: []Tag{
+			{Key: "k-aaa",
+				Value: "v-aaa",
+			},
+		},
+		Count: 2,
+	}
+
+	response, err := client.ScaleOutKubernetesCluster(TestClusterId, request)
+	if err != nil {
+		t.Fatalf("Error %++v", err)
+	} else {
+		t.Logf("Response %++v", response)
+	}
+}
+
+func Test_DeleteKubernetesClusterNodes(t *testing.T) {
+	client := NewTestClientForDebug()
+
+	request := &DeleteKubernetesClusterNodesRequest{
+		ReleaseNode: false,
+		Nodes:       []string{"cn-beijing.192.168.0.128"},
+	}
+
+	response, err := client.DeleteKubernetesClusterNodes(TestClusterId, request)
+	if err != nil {
+		t.Fatalf("Error %++v", err)
+	} else {
+		t.Logf("Response %++v", response)
+	}
+}
diff --git a/cs/kubernets_cluster.go b/cs/kubernets_cluster.go
new file mode 100644
index 0000000..76b2824
--- /dev/null
+++ b/cs/kubernets_cluster.go
@@ -0,0 +1,496 @@
+package cs
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"time"
+
+	"github.com/denverdino/aliyungo/ecs"
+
+	"github.com/denverdino/aliyungo/common"
+)
+
+type TaskState string
+
+const (
+	// task status
+	Task_Status_Running = "running"
+	Task_Status_Success = "Success"
+	Task_Status_Failed  = "Failed"
+
+	// upgrade state
+	UpgradeStep_NotStart    = "not_start"
+	UpgradeStep_Prechecking = "prechecking"
+	UpgradeStep_Upgrading   = "upgrading"
+	UpgradeStep_Pause       = "pause"
+	UpgradeStep_Success     = "success"
+)
+
+type WeeklyPeriod string
+type MaintenanceTime string
+
+// maintenance window
+type MaintenanceWindow struct {
+	Enable          bool            `json:"enable"`
+	MaintenanceTime MaintenanceTime `json:"maintenance_time"`
+	Duration        string          `json:"duration"`
+	Recurrence      string          `json:"recurrence,omitempty"`
+	WeeklyPeriod    WeeklyPeriod    `json:"weekly_period"`
+}
+
+//modify cluster,include DeletionProtection and so on
+type ModifyClusterArgs struct {
+	DeletionProtection bool              `json:"deletion_protection"`
+	ResourceGroupId    string            `json:"resource_group_id"`
+	MaintenanceWindow  MaintenanceWindow `json:"maintenance_window"`
+	EnableRRSA         bool              `json:"enable_rrsa"`
+}
+
+type UpgradeClusterArgs struct {
+	Version string `json:"version"`
+}
+
+type UpgradeClusterResult struct {
+	Status           TaskState `json:"status"`
+	PrecheckReportId string    `json:"precheck_report_id"`
+	UpgradeStep      string    `json:"upgrade_step"`
+	ErrorMessage     string    `json:"error_message"`
+	*UpgradeTask     `json:"upgrade_task,omitempty"`
+}
+
+type UpgradeTask struct {
+	FieldRetries    int           `json:"retries,omitempty"`
+	FieldCreatedAt  time.Time     `json:"created_at"`
+	FieldMessage    string        `json:"message,omitempty"`
+	FieldStatus     string        `json:"status"` // empty|running|success|failed
+	FieldFinishedAt time.Time     `json:"finished_at,omitempty"`
+	UpgradeStatus   UpgradeStatus `json:"upgrade_status"`
+}
+
+type UpgradeStatus struct {
+	State      string  `json:"state"`
+	Phase      string  `json:"phase"` // {Master1, Master2, Master3, Nodes}
+	Total      int     `json:"total"`
+	Succeeded  int     `json:"succeeded"`
+	Failed     string  `json:"failed"`
+	Events     []Event `json:"events"`
+	IsCanceled bool    `json:"is_canceled"`
+}
+
+type Event struct {
+	Timestamp time.Time
+	Type      string
+	Reason    string
+	Message   string
+	Source    string
+}
+
+//modify cluster
+func (client *Client) ModifyCluster(clusterId string, args *ModifyClusterArgs) error {
+	return client.Invoke("", http.MethodPut, "/api/v2/clusters/"+clusterId, nil, args, nil)
+}
+
+//upgrade cluster
+func (client *Client) UpgradeCluster(clusterId string, args *UpgradeClusterArgs) error {
+	return client.Invoke("", http.MethodPost, fmt.Sprintf("/api/v2/clusters/%s/upgrade", clusterId), nil, args, nil)
+}
+
+//cancel upgrade cluster
+func (client *Client) CancelUpgradeCluster(clusterId string) error {
+	return client.Invoke("", http.MethodPost, fmt.Sprintf("/api/v2/clusters/%s/upgrade/cancel", clusterId), nil, nil, nil)
+}
+
+func (client *Client) QueryUpgradeClusterResult(clusterId string) (*UpgradeClusterResult, error) {
+	cluster := &UpgradeClusterResult{}
+	err := client.Invoke("", http.MethodGet, fmt.Sprintf("/api/v2/clusters/%s/upgrade/status", clusterId), nil, nil, cluster)
+	if err != nil {
+		return nil, err
+	}
+	return cluster, nil
+}
+
+//Cluster Info
+type KubernetesClusterType string
+
+var (
+	DelicatedKubernetes = KubernetesClusterType("Kubernetes")
+	ManagedKubernetes   = KubernetesClusterType("ManagedKubernetes")
+)
+
+type KubernetesClusterProfile string
+
+var (
+	Profile4Serverless = KubernetesClusterProfile("Serverless")
+)
+
+type ProxyMode string
+
+var (
+	IPTables = "iptables"
+	IPVS     = "ipvs"
+)
+
+type Effect string
+
+var (
+	TaintNoExecute        = "NoExecute"
+	TaintNoSchedule       = "NoSchedule"
+	TaintPreferNoSchedule = "PreferNoSchedule"
+)
+
+type ClusterArgs struct {
+	DisableRollback bool `json:"disable_rollback"`
+	TimeoutMins     int  `json:"timeout_mins"`
+
+	Name               string                `json:"name"`
+	ClusterType        KubernetesClusterType `json:"cluster_type"`
+	Profile            string                `json:"profile"`
+	KubernetesVersion  string                `json:"kubernetes_version"`
+	DeletionProtection bool                  `json:"deletion_protection"`
+	EnableRRSA         bool                  `json:"enable_rrsa"`
+
+	NodeCidrMask string `json:"node_cidr_mask"`
+	UserCa       string `json:"user_ca"`
+
+	OsType   string `json:"os_type"`
+	Platform string `json:"platform"`
+
+	UserData string `json:"user_data"`
+
+	NodePortRange string `json:"node_port_range"`
+	NodeNameMode  string `json:"node_name_mode"`
+
+	//ImageId
+	ImageId string `json:"image_id"`
+
+	PodVswitchIds []string `json:"pod_vswitch_ids"` // eni多网卡模式下,需要传额外的vswitchid给addon
+
+	LoginPassword string `json:"login_password"` //和KeyPair 二选一
+	KeyPair       string `json:"key_pair"`       ////LoginPassword 二选一
+
+	RegionId      common.Region `json:"region_id"`
+	VpcId         string        `json:"vpcid"`
+	ContainerCidr string        `json:"container_cidr"`
+	ServiceCidr   string        `json:"service_cidr"`
+
+	CloudMonitorFlags bool `json:"cloud_monitor_flags"`
+
+	SecurityGroupId           string    `json:"security_group_id"`
+	IsEnterpriseSecurityGroup bool      `json:"is_enterprise_security_group"`
+	EndpointPublicAccess      bool      `json:"endpoint_public_access"`
+	LoadBalancerSpec          string    `json:"load_balancer_spec"` //api server slb实例规格
+	ProxyMode                 ProxyMode `json:"proxy_mode"`
+	SnatEntry                 bool      `json:"snat_entry"`
+	ResourceGroupId           string    `json:"resource_group_id"`
+
+	Addons []Addon `json:"addons"`
+	Tags   []Tag   `json:"tags"`
+
+	Taints []Taint `json:"taints"`
+
+	ApiAudiences          string            `json:"api_audiences,omitempty"`
+	ServiceAccountIssuer  string            `json:"service_account_issuer,omitempty"`
+	CustomSAN             string            `json:"custom_san,omitempty"`
+	ClusterSpec           string            `json:"cluster_spec"`
+	Timezone              string            `json:"timezone"`
+	ClusterDomain         string            `json:"cluster_domain"`
+	RdsInstances          []string          `json:"rds_instances"`
+	EncryptionProviderKey string            `json:"encryption_provider_key"`
+	MaintenanceWindow     MaintenanceWindow `json:"maintenance_window"`
+
+	//controlplane log parms
+	ControlplaneLogProject string   `json:"controlplane_log_project"`
+	ControlplaneLogTTL     string   `json:"controlplane_log_ttl"`
+	ControlplaneComponents []string `json:"controlplane_log_components"`
+
+	// Operating system hardening
+	SocEnabled bool `json:"soc_enabled"`
+	CisEnabled bool `json:"cis_enabled"`
+}
+
+//addon
+type Addon struct {
+	Name     string `json:"name"`
+	Version  string `json:"version"`
+	Config   string `json:"config"`
+	Disabled bool   `json:"disabled"` // 是否禁止默认安装
+}
+
+//taint
+type Taint struct {
+	Key    string `json:"key"`
+	Value  string `json:"value"`
+	Effect Effect `json:"effect"`
+}
+
+type Label struct {
+	Key   string `json:"key"`
+	Value string `json:"value"`
+}
+
+type MasterArgs struct {
+	MasterCount         int      `json:"master_count"`
+	MasterVSwitchIds    []string `json:"master_vswitch_ids"`
+	MasterInstanceTypes []string `json:"master_instance_types"`
+
+	MasterInstanceChargeType string `json:"master_instance_charge_type"`
+	MasterPeriod             int    `json:"master_period"`
+	MasterPeriodUnit         string `json:"master_period_unit"`
+
+	MasterAutoRenew       bool `json:"master_auto_renew"`
+	MasterAutoRenewPeriod int  `json:"master_auto_renew_period"`
+
+	MasterSystemDiskCategory         ecs.DiskCategory `json:"master_system_disk_category"`
+	MasterSystemDiskSize             int64            `json:"master_system_disk_size"`
+	MasterSystemDiskPerformanceLevel string           `json:"master_system_disk_performance_level"`
+
+	MasterDataDisks []DataDisk `json:"master_data_disks"` //支持多个数据盘
+
+	//support hpc/scc
+	MasterHpcClusterId    string `json:"master_hpc_cluster_id"`
+	MasterDeploymentSetId string `json:"master_deploymentset_id"`
+
+	//master node deletion protection
+	MasterDeletionProtection *bool `json:"master_deletion_protection"`
+
+	// disk snapshot policy
+	MasterSnapshotPolicyId string `json:"master_system_disk_snapshot_policy_id"`
+}
+
+type WorkerArgs struct {
+	WorkerVSwitchIds    []string `json:"worker_vswitch_ids"`
+	WorkerInstanceTypes []string `json:"worker_instance_types"`
+
+	NumOfNodes int64 `json:"num_of_nodes"`
+
+	WorkerInstanceChargeType string `json:"worker_instance_charge_type"`
+	WorkerPeriod             int    `json:"worker_period"`
+	WorkerPeriodUnit         string `json:"worker_period_unit"`
+
+	WorkerAutoRenew       bool `json:"worker_auto_renew"`
+	WorkerAutoRenewPeriod int  `json:"worker_auto_renew_period"`
+
+	WorkerSystemDiskCategory         ecs.DiskCategory `json:"worker_system_disk_category"`
+	WorkerSystemDiskSize             int64            `json:"worker_system_disk_size"`
+	WorkerSystemDiskPerformanceLevel string           `json:"worker_system_disk_performance_level"`
+
+	WorkerDataDisk  bool       `json:"worker_data_disk"`
+	WorkerDataDisks []DataDisk `json:"worker_data_disks"` //支持多个数据盘
+
+	WorkerHpcClusterId    string `json:"worker_hpc_cluster_id"`
+	WorkerDeploymentSetId string `json:"worker_deploymentset_id"`
+
+	//worker node deletion protection
+	WorkerDeletionProtection *bool `json:"worker_deletion_protection"`
+
+	//Runtime only for worker nodes
+	Runtime Runtime `json:"runtime"`
+
+	// disk snapshot policy
+	WorkerSnapshotPolicyId string `json:"worker_system_disk_snapshot_policy_id"`
+}
+
+type ScaleOutKubernetesClusterRequest struct {
+	LoginPassword string `json:"login_password"` //和KeyPair 二选一
+	KeyPair       string `json:"key_pair"`       ////LoginPassword 二选一
+
+	WorkerVSwitchIds    []string `json:"worker_vswitch_ids"`
+	WorkerInstanceTypes []string `json:"worker_instance_types"`
+
+	WorkerInstanceChargeType string `json:"worker_instance_charge_type"`
+	WorkerPeriod             int    `json:"worker_period"`
+	WorkerPeriodUnit         string `json:"worker_period_unit"`
+
+	WorkerAutoRenew       bool `json:"worker_auto_renew"`
+	WorkerAutoRenewPeriod int  `json:"worker_auto_renew_period"`
+
+	WorkerSystemDiskCategory         ecs.DiskCategory `json:"worker_system_disk_category"`
+	WorkerSystemDiskSize             int64            `json:"worker_system_disk_size"`
+	WorkerSnapshotPolicyId           string           `json:"worker_system_disk_snapshot_policy_id"`
+	WorkerSystemDiskPerformanceLevel string           `json:"worker_system_disk_performance_level"`
+
+	WorkerDataDisk  bool       `json:"worker_data_disk"`
+	WorkerDataDisks []DataDisk `json:"worker_data_disks"` //支持多个数据盘
+
+	Tags    []Tag   `json:"tags"`
+	Taints  []Taint `json:"taints"`
+	ImageId string  `json:"image_id"`
+
+	UserData string `json:"user_data"`
+
+	Count             int64    `json:"count"`
+	CpuPolicy         string   `json:"cpu_policy"`
+	Runtime           Runtime  `json:"runtime"`
+	CloudMonitorFlags bool     `json:"cloud_monitor_flags"`
+	RdsInstances      []string ` json:"rds_instances"`
+}
+
+//datadiks
+type DataDisk struct {
+	Category             string `json:"category"`
+	KMSKeyId             string `json:"kms_key_id"`
+	Encrypted            string `json:"encrypted"` // true|false
+	Device               string `json:"device"`    //  could be /dev/xvd[a-z]. If not specification, will use default value.
+	Size                 string `json:"size"`
+	DiskName             string `json:"name"`
+	AutoSnapshotPolicyId string `json:"auto_snapshot_policy_id"`
+	PerformanceLevel     string `json:"performance_Level"`
+}
+
+type NodePoolDataDisk struct {
+	Category             string `json:"category"`
+	KMSKeyId             string `json:"kms_key_id"`
+	Encrypted            string `json:"encrypted"` // true|false
+	Device               string `json:"device"`    //  could be /dev/xvd[a-z]. If not specification, will use default value.
+	Size                 int    `json:"size"`
+	DiskName             string `json:"name"`
+	AutoSnapshotPolicyId string `json:"auto_snapshot_policy_id"`
+	PerformanceLevel     string `json:"performance_Level"`
+}
+
+//runtime
+type Runtime struct {
+	Name                       string   `json:"name"`
+	Version                    string   `json:"version"`
+	RuntimeClass               []string `json:"runtimeClass,omitempty"`
+	Exist                      bool     `json:"exist"`
+	AvailableNetworkComponents []string `json:"availableNetworkComponents,omitempty"`
+}
+
+//DelicatedKubernetes
+type DelicatedKubernetesClusterCreationRequest struct {
+	ClusterArgs
+	MasterArgs
+	WorkerArgs
+}
+
+//ManagedKubernetes
+type ManagedKubernetesClusterCreationRequest struct {
+	ClusterArgs
+	WorkerArgs
+}
+
+//Validate
+
+//Create DelicatedKubernetes Cluster
+func (client *Client) CreateDelicatedKubernetesCluster(request *DelicatedKubernetesClusterCreationRequest) (*ClusterCommonResponse, error) {
+	response := &ClusterCommonResponse{}
+	path := "/clusters"
+	if request.ResourceGroupId != "" {
+		// 创建集群到指定资源组
+		path = fmt.Sprintf("/resource_groups/%s/clusters", request.ResourceGroupId)
+	}
+	err := client.Invoke(request.RegionId, http.MethodPost, path, nil, request, response)
+	if err != nil {
+		return nil, err
+	}
+
+	return response, nil
+}
+
+//Create ManagedKubernetes Cluster
+func (client *Client) CreateManagedKubernetesCluster(request *ManagedKubernetesClusterCreationRequest) (*ClusterCommonResponse, error) {
+	response := &ClusterCommonResponse{}
+	path := "/clusters"
+	if request.ResourceGroupId != "" {
+		// 创建集群到指定资源组
+		path = fmt.Sprintf("/resource_groups/%s/clusters", request.ResourceGroupId)
+	}
+	err := client.Invoke(request.RegionId, http.MethodPost, path, nil, request, response)
+	if err != nil {
+		return nil, err
+	}
+
+	return response, nil
+}
+
+//ScaleKubernetesCluster
+func (client *Client) ScaleOutKubernetesCluster(clusterId string, request *ScaleOutKubernetesClusterRequest) (*ClusterCommonResponse, error) {
+	response := &ClusterCommonResponse{}
+	err := client.Invoke("", http.MethodPost, fmt.Sprintf("/api/v2/clusters/%s", clusterId), nil, request, response)
+	if err != nil {
+		return nil, err
+	}
+
+	return response, nil
+}
+
+//DeleteClusterNodes
+type DeleteKubernetesClusterNodesRequest struct {
+	ReleaseNode bool     `json:"release_node"` //if set to true, the ecs instance will be released
+	Nodes       []string `json:"nodes"`        //the format is regionId.instanceId|Ip ,for example  cn-hangzhou.192.168.1.2 or cn-hangzhou.i-abc
+	DrainNode   bool     `json:"drain_node"`   //same as Nodes
+}
+
+//DeleteClusterNodes
+func (client *Client) DeleteKubernetesClusterNodes(clusterId string, request *DeleteKubernetesClusterNodesRequest) (*common.Response, error) {
+	response := &common.Response{}
+	err := client.Invoke("", http.MethodPost, fmt.Sprintf("/api/v2/clusters/%s/nodes/remove", clusterId), nil, request, response)
+	if err != nil {
+		return nil, err
+	}
+
+	return response, nil
+}
+
+//Cluster definition
+type KubernetesClusterDetail struct {
+	RegionId common.Region `json:"region_id"`
+
+	Name        string                `json:"name"`
+	ClusterId   string                `json:"cluster_id"`
+	Size        int64                 `json:"size"`
+	ClusterType KubernetesClusterType `json:"cluster_type"`
+	Profile     string                `json:"profile"`
+
+	VpcId                 string `json:"vpc_id"`
+	VSwitchIds            string `json:"vswitch_id"`
+	SecurityGroupId       string `json:"security_group_id"`
+	IngressLoadbalancerId string `json:"external_loadbalancer_id"`
+	ResourceGroupId       string `json:"resource_group_id"`
+	NetworkMode           string `json:"network_mode"`
+	ContainerCIDR         string `json:"subnet_cidr"`
+
+	Tags  []Tag  `json:"tags"`
+	State string `json:"state"`
+
+	InitVersion        string `json:"init_version"`
+	CurrentVersion     string `json:"current_version"`
+	PrivateZone        bool   `json:"private_zone"`
+	DeletionProtection bool   `json:"deletion_protection"`
+	EnableRRSA         bool   `json:"enable_rrsa"`
+	MetaData           string `json:"meta_data"`
+
+	Created time.Time `json:"created"`
+	Updated time.Time `json:"updated"`
+
+	WorkerRamRoleName string                 `json:"worker_ram_role_name"`
+	ClusterSpec       string                 `json:"cluster_spec"`
+	OSType            string                 `json:"os_type"`
+	MasterURL         string                 `json:"master_url"`
+	MaintenanceWindow MaintenanceWindow      `json:"maintenance_window"`
+	Parameters        map[string]interface{} `json:"parameters"`
+}
+
+//GetMetaData
+func (c *KubernetesClusterDetail) GetMetaData() map[string]interface{} {
+	m := make(map[string]interface{})
+	_ = json.Unmarshal([]byte(c.MetaData), &m)
+	return m
+}
+
+//查询集群详情
+func (client *Client) DescribeKubernetesClusterDetail(clusterId string) (*KubernetesClusterDetail, error) {
+	cluster := &KubernetesClusterDetail{}
+	err := client.Invoke("", http.MethodGet, "/clusters/"+clusterId, nil, nil, cluster)
+	if err != nil {
+		return nil, err
+	}
+	return cluster, nil
+}
+
+//DeleteKubernetesCluster
+func (client *Client) DeleteKubernetesCluster(clusterId string) error {
+	return client.Invoke("", http.MethodDelete, "/clusters/"+clusterId, nil, nil, nil)
+}
diff --git a/cs/node_pool.go b/cs/node_pool.go
new file mode 100644
index 0000000..802a3de
--- /dev/null
+++ b/cs/node_pool.go
@@ -0,0 +1,256 @@
+package cs
+
+import (
+	"fmt"
+	"github.com/denverdino/aliyungo/common"
+	"github.com/denverdino/aliyungo/ecs"
+	"net/http"
+	"net/url"
+	"time"
+)
+
+type SpotPrice struct {
+	InstanceType string `json:"instance_type"`
+	PriceLimit   string `json:"price_limit"`
+}
+
+type NodePoolInfo struct {
+	NodePoolId      string        `json:"nodepool_id"`
+	RegionId        common.Region `json:"region_id"`
+	Name            *string       `json:"name"`
+	Created         time.Time     `json:"created"`
+	Updated         time.Time     `json:"updated"`
+	IsDefault       *bool         `json:"is_default"`
+	NodePoolType    string        `json:"type"`
+	ResourceGroupId *string       `json:"resource_group_id"`
+}
+
+type ScalingGroup struct {
+	VpcId                      string             `json:"vpc_id"`
+	VswitchIds                 []string           `json:"vswitch_ids"`
+	InstanceTypes              []string           `json:"instance_types"`
+	LoginPassword              *string            `json:"login_password"`
+	KeyPair                    *string            `json:"key_pair"`
+	SecurityGroupId            string             `json:"security_group_id"`
+	SecurityGroupIds           []string           `json:"security_group_ids"`
+	SystemDiskCategory         ecs.DiskCategory   `json:"system_disk_category"`
+	SystemDiskSize             *int64             `json:"system_disk_size"`
+	SystemDiskPerformanceLevel *string            `json:"system_disk_performance_level"`
+	SystemDiskEncryptAlgorithm *string            `json:"system_disk_encrypt_algorithm"`
+	SystemDiskEncrypted        *bool              `json:"system_disk_encrypted"`
+	SystemDiskKMSKeyId         *string            `json:"system_disk_kms_key_id"`
+	DataDisks                  []NodePoolDataDisk `json:"data_disks"` //支持多个数据盘
+	Tags                       []Tag              `json:"tags"`
+	ImageId                    *string            `json:"image_id"`
+	Platform                   *string            `json:"platform"`
+	OSType                     *string            `json:"os_type"`
+	ImageType                  *string            `json:"image_type"`
+	InstanceChargeType         *string            `json:"instance_charge_type"`
+	Period                     *int               `json:"period"`
+	PeriodUnit                 *string            `json:"period_unit"`
+	AutoRenew                  *bool              `json:"auto_renew"`
+	AutoRenewPeriod            *int               `json:"auto_renew_period"`
+	// spot实例
+	SpotStrategy   *string     `json:"spot_strategy"`
+	SpotPriceLimit []SpotPrice `json:"spot_price_limit"`
+
+	RdsInstances   []string `json:"rds_instances"`
+	ScalingPolicy  *string  `json:"scaling_policy"`
+	ScalingGroupId *string  `json:"scaling_group_id"`
+
+	WorkerSnapshotPolicyId *string `json:"worker_system_disk_snapshot_policy_id"`
+	// 公网ip
+	InternetChargeType      *string `json:"internet_charge_type"`
+	InternetMaxBandwidthOut *int    `json:"internet_max_bandwidth_out"`
+	// Operating system hardening
+	SocEnabled *bool `json:"soc_enabled"`
+	CisEnabled *bool `json:"cis_enabled"`
+	// ipv6
+	SupportIPv6 *bool `json:"support_ipv6"`
+	// deploymentset
+	DeploymentSetId *string `json:"deploymentset_id"`
+	DesiredSize     *int64  `json:"desired_size,omitempty"`
+}
+
+type AutoScaling struct {
+	Enable       *bool   `json:"enable"`
+	MaxInstances *int64  `json:"max_instances"`
+	MinInstances *int64  `json:"min_instances"`
+	Type         *string `json:"type"`
+	// eip
+	IsBindEip *bool `json:"is_bond_eip"`
+	// value: PayByBandwidth / PayByTraffic
+	EipInternetChargeType *string `json:"eip_internet_charge_type"`
+	// default 5
+	EipBandWidth *int64 `json:"eip_bandwidth"`
+}
+
+type KubernetesConfig struct {
+	NodeNameMode string  `json:"node_name_mode"`
+	Taints       []Taint `json:"taints"`
+	Labels       []Label `json:"labels"`
+	CpuPolicy    string  `json:"cpu_policy"`
+	UserData     *string `json:"user_data"`
+
+	Runtime           string `json:"runtime,omitempty"`
+	RuntimeVersion    string `json:"runtime_version"`
+	CmsEnabled        *bool  `json:"cms_enabled"`
+	OverwriteHostname *bool  `json:"overwrite_hostname"`
+	Unschedulable     *bool  `json:"unschedulable"`
+}
+
+// 加密计算节点池
+type TEEConfig struct {
+	TEEType   string `json:"tee_type"`
+	TEEEnable bool   `json:"tee_enable"`
+}
+
+// 托管节点池配置
+type Management struct {
+	Enable      *bool       `json:"enable"`
+	AutoRepair  *bool       `json:"auto_repair"`
+	UpgradeConf UpgradeConf `json:"upgrade_config"`
+}
+
+type UpgradeConf struct {
+	AutoUpgrade       *bool  `json:"auto_upgrade"`
+	Surge             *int64 `json:"surge"`
+	SurgePercentage   *int64 `json:"surge_percentage,omitempty"`
+	MaxUnavailable    *int64 `json:"max_unavailable"`
+	KeepSurgeOnFailed *bool  `json:"keep_surge_on_failed"`
+}
+
+type NodeConfig struct {
+	KubeletConfiguration *KubeletConfiguration `json:"kubelet_configuration,omitempty"`
+	RolloutPolicy        *RolloutPolicy        `json:"rollout_policy,omitempty"`
+}
+
+type KubeletConfiguration struct {
+	CpuManagerPolicy        *string                `json:"cpuManagerPolicy,omitempty"`
+	EventBurst              *int64                 `json:"eventBurst,omitempty"`
+	EventRecordQPS          *int64                 `json:"eventRecordQPS,omitempty"`
+	EvictionHard            map[string]interface{} `json:"evictionHard,omitempty"`
+	EvictionSoft            map[string]interface{} `json:"evictionSoft,omitempty"`
+	EvictionSoftGracePeriod map[string]interface{} `json:"evictionSoftGracePeriod,omitempty"`
+	KubeAPIBurst            *int64                 `json:"kubeAPIBurst,omitempty"`
+	KubeAPIQPS              *int64                 `json:"kubeAPIQPS,omitempty"`
+	KubeReserved            map[string]interface{} `json:"kubeReserved,omitempty"`
+	RegistryBurst           *int64                 `json:"registryBurst,omitempty"`
+	RegistryPullQPS         *int64                 `json:"registryPullQPS,omitempty"`
+	SerializeImagePulls     *bool                  `json:"serializeImagePulls,omitempty"`
+	SystemReserved          map[string]interface{} `json:"systemReserved,omitempty"`
+}
+
+type RolloutPolicy struct {
+	MaxUnavailable *int64 `json:"max_unavailable,omitempty"`
+}
+
+type CreateNodePoolRequest struct {
+	RegionId         common.Region `json:"region_id"`
+	Count            int64         `json:"count"`
+	NodePoolInfo     `json:"nodepool_info"`
+	ScalingGroup     `json:"scaling_group"`
+	KubernetesConfig `json:"kubernetes_config"`
+	AutoScaling      `json:"auto_scaling"`
+	TEEConfig        `json:"tee_config"`
+	Management       `json:"management"`
+	NodeConfig       *NodeConfig `json:"node_config,omitempty"`
+}
+
+type BasicNodePool struct {
+	NodePoolInfo   `json:"nodepool_info"`
+	NodePoolStatus `json:"status"`
+}
+
+type NodePoolStatus struct {
+	TotalNodes   int `json:"total_nodes"`
+	OfflineNodes int `json:"offline_nodes"`
+	ServingNodes int `json:"serving_nodes"`
+	//DesiredNodes int  `json:"desired_nodes"`
+	RemovingNodes int    `json:"removing_nodes"`
+	FailedNodes   int    `json:"failed_nodes"`
+	InitialNodes  int    `json:"initial_nodes"`
+	HealthyNodes  int    `json:"healthy_nodes"`
+	State         string `json:"state"`
+}
+
+type NodePoolDetail struct {
+	BasicNodePool
+	KubernetesConfig `json:"kubernetes_config"`
+	ScalingGroup     `json:"scaling_group"`
+	AutoScaling      `json:"auto_scaling"`
+	TEEConfig        `json:"tee_config"`
+	Management       `json:"management"`
+}
+
+type CreateNodePoolResponse struct {
+	Response
+	NodePoolID string `json:"nodepool_id"`
+	Message    string `json:"Message"`
+	TaskID     string `json:"task_id"`
+}
+
+type UpdateNodePoolRequest struct {
+	RegionId         common.Region `json:"region_id"`
+	Count            int64         `json:"count"`
+	NodePoolInfo     `json:"nodepool_info"`
+	ScalingGroup     `json:"scaling_group"`
+	KubernetesConfig `json:"kubernetes_config"`
+	AutoScaling      `json:"auto_scaling"`
+	Management       `json:"management"`
+	NodeConfig       *NodeConfig `json:"node_config,omitempty"`
+}
+
+type NodePoolsDetail struct {
+	Response
+	NodePools []NodePoolDetail `json:"nodepools"`
+}
+
+func (client *Client) CreateNodePool(request *CreateNodePoolRequest, clusterId string) (*CreateNodePoolResponse, error) {
+	response := &CreateNodePoolResponse{}
+	err := client.Invoke(request.RegionId, http.MethodPost, fmt.Sprintf("/clusters/%s/nodepools", clusterId), nil, request, response)
+	if err != nil {
+		return nil, err
+	}
+
+	return response, nil
+}
+
+func (client *Client) DescribeNodePoolDetail(clusterId, nodePoolId string) (*NodePoolDetail, error) {
+	nodePool := &NodePoolDetail{}
+	err := client.Invoke("", http.MethodGet, fmt.Sprintf("/clusters/%s/nodepools/%s", clusterId, nodePoolId), nil, nil, nodePool)
+	if err != nil {
+		return nil, err
+	}
+	return nodePool, nil
+}
+
+func (client *Client) DescribeClusterNodePools(clusterId string) (*[]NodePoolDetail, error) {
+	nodePools := &NodePoolsDetail{}
+	err := client.Invoke("", http.MethodGet, fmt.Sprintf("/clusters/%s/nodepools", clusterId), nil, nil, nodePools)
+	if err != nil {
+		return nil, err
+	}
+	return &nodePools.NodePools, nil
+}
+
+func (client *Client) UpdateNodePool(clusterId string, nodePoolId string, request *UpdateNodePoolRequest) (*Response, error) {
+	response := &Response{}
+	err := client.Invoke(request.RegionId, http.MethodPut, fmt.Sprintf("/clusters/%s/nodepools/%s", clusterId, nodePoolId), nil, request, response)
+	if err != nil {
+		return nil, err
+	}
+
+	return response, nil
+}
+
+//Deprecated
+func (client *Client) DeleteNodePool(clusterId, nodePoolId string) error {
+	return client.Invoke("", http.MethodDelete, fmt.Sprintf("/clusters/%s/nodepools/%s", clusterId, nodePoolId), nil, nil, nil)
+}
+
+func (client *Client) ForceDeleteNodePool(clusterId, nodePoolId string) error {
+	query := url.Values{}
+	query.Add("force", "true")
+	return client.Invoke("", http.MethodDelete, fmt.Sprintf("/clusters/%s/nodepools/%s", clusterId, nodePoolId), query, nil, nil)
+}
diff --git a/cs/node_pool_test.go b/cs/node_pool_test.go
new file mode 100644
index 0000000..4930459
--- /dev/null
+++ b/cs/node_pool_test.go
@@ -0,0 +1,101 @@
+package cs
+
+import (
+	"os"
+	"strings"
+	"testing"
+)
+
+var (
+	VpcId         = os.Getenv("VpcId")
+	VswitchIds    = os.Getenv("VswitchIds")
+	LoginPassword = os.Getenv("LoginPassword")
+	NodePoolId    = os.Getenv("NodePoolId")
+
+	nodepoolName   = "test-npl"
+	systemDiskSize = int64(120)
+)
+
+func Test_CreateNodePool(t *testing.T) {
+	client := NewTestClientForDebug()
+
+	args := &CreateNodePoolRequest{
+		Count:    1,
+		RegionId: TestRegionID,
+		NodePoolInfo: NodePoolInfo{
+			Name:         &nodepoolName,
+			NodePoolType: "ess",
+		},
+		ScalingGroup: ScalingGroup{
+			VpcId:              VpcId,
+			VswitchIds:         strings.Split(VswitchIds, ","),
+			InstanceTypes:      []string{"ecs.n6.large"},
+			LoginPassword:      &LoginPassword,
+			SystemDiskCategory: "cloud_efficiency",
+			SystemDiskSize:     &systemDiskSize,
+			DataDisks:          []NodePoolDataDisk{{Size: 100, Category: "cloud_ssd"}},
+		},
+		KubernetesConfig: KubernetesConfig{
+			NodeNameMode: "customized,aliyun.com,5,test",
+		},
+	}
+
+	resp, err := client.CreateNodePool(args, TestClusterId)
+	if err != nil {
+		t.Errorf("Error %++v", err)
+	} else {
+		t.Logf("response: %++v", resp)
+	}
+}
+
+func Test_DescribeNodePoolDetail(t *testing.T) {
+	client := NewTestClientForDebug()
+
+	resp, err := client.DescribeNodePoolDetail(TestClusterId, NodePoolId)
+	if err != nil {
+		t.Errorf("Error %++v", err)
+	} else {
+		t.Logf("response: %++v", resp)
+	}
+}
+
+func Test_UpdateNodePool(t *testing.T) {
+	client := NewTestClientForDebug()
+
+	args := &UpdateNodePoolRequest{
+		Count:    2,
+		RegionId: TestRegionID,
+		ScalingGroup: ScalingGroup{
+			InstanceTypes: []string{"ecs.n4.large"},
+		},
+	}
+
+	resp, err := client.UpdateNodePool(TestClusterId, NodePoolId, args)
+	if err != nil {
+		t.Errorf("Error %++v", err)
+	} else {
+		t.Logf("response: %++v", resp)
+	}
+}
+
+func Test_DeleteNodePool(t *testing.T) {
+	client := NewTestClientForDebug()
+
+	err := client.DeleteNodePool(TestClusterId, NodePoolId)
+	if err != nil {
+		t.Errorf("Error %++v", err)
+	} else {
+		t.Logf("success")
+	}
+}
+
+func Test_DescribeClusterNodePools(t *testing.T) {
+	client := NewTestClientForDebug()
+
+	resp, err := client.DescribeClusterNodePools(TestClusterId)
+	if err != nil {
+		t.Errorf("Error %++v", err)
+	} else {
+		t.Logf("response: %++v", resp)
+	}
+}
diff --git a/cs/projects_client.go b/cs/projects_client.go
index 58d0ea9..7df504a 100644
--- a/cs/projects_client.go
+++ b/cs/projects_client.go
@@ -67,6 +67,14 @@ func (client *ProjectClient) SetUserAgent(userAgent string) {
 	client.userAgent = userAgent
 }
 
+// SetTransport sets transport to the http client
+func (client *ProjectClient) SetTransport(transport http.RoundTripper) {
+	if client.httpClient == nil {
+		client.httpClient = &http.Client{}
+	}
+	client.httpClient.Transport = transport
+}
+
 func (client *ProjectClient) ClusterId() string {
 	return client.clusterId
 }
@@ -134,13 +142,13 @@ func (client *ProjectClient) Invoke(method string, path string, query url.Values
 
 	if client.debug {
 		var prettyJSON bytes.Buffer
-		err = json.Indent(&prettyJSON, body, "", "    ")
-		log.Println(string(prettyJSON.Bytes()))
+		_ = json.Indent(&prettyJSON, body, "", "    ")
+		log.Println(prettyJSON.String())
 	}
 
 	if statusCode >= 400 && statusCode <= 599 {
 		errorResponse := common.ErrorResponse{}
-		err = json.Unmarshal(body, &errorResponse)
+		_ = json.Unmarshal(body, &errorResponse)
 		ecsError := &common.Error{
 			ErrorResponse: errorResponse,
 			StatusCode:    statusCode,
diff --git a/cs/serverless.go b/cs/serverless.go
new file mode 100644
index 0000000..201c7f1
--- /dev/null
+++ b/cs/serverless.go
@@ -0,0 +1,145 @@
+package cs
+
+import (
+	"fmt"
+	"net/http"
+	"net/url"
+	"strconv"
+	"time"
+
+	"github.com/denverdino/aliyungo/common"
+)
+
+type ServerlessCreationArgs struct {
+	ClusterType           KubernetesClusterType    `json:"cluster_type"`
+	Profile               KubernetesClusterProfile `json:"profile"`
+	Name                  string                   `json:"name"`
+	RegionId              string                   `json:"region_id"`
+	VpcId                 string                   `json:"vpcid"`
+	VSwitchId             string                   `json:"vswitch_id"`
+	VswitchIds            []string                 `json:"vswitch_ids"`
+	EndpointPublicAccess  bool                     `json:"public_slb"`
+	PrivateZone           bool                     `json:"private_zone"`
+	NatGateway            bool                     `json:"nat_gateway"`
+	KubernetesVersion     string                   `json:"kubernetes_version"`
+	DeletionProtection    bool                     `json:"deletion_protection"`
+	EnableRRSA            bool                     `json:"enable_rrsa"`
+	SecurityGroupId       string                   `json:"security_group_id"`
+	Tags                  []Tag                    `json:"tags"`
+	Addons                []Addon                  `json:"addons"`
+	ResourceGroupId       string                   `json:"resource_group_id"`
+	ClusterSpec           string                   `json:"cluster_spec"`
+	LoadBalancerSpec      string                   `json:"load_balancer_spec"` //api server slb实例规格
+	ServiceCIDR           string                   `json:"service_cidr"`
+	TimeZone              string                   `json:"timezone"`
+	ServiceDiscoveryTypes []string                 `json:"service_discovery_types"`
+	ZoneID                string                   `json:"zone_id"`
+	LoggingType           string                   `json:"logging_type"`
+	SLSProjectName        string                   `json:"sls_project_name"`
+	SnatEntry             bool                     `json:"snat_entry"`
+}
+
+type ServerlessClusterResponse struct {
+	ClusterId          string                `json:"cluster_id"`
+	Name               string                `json:"name"`
+	ClusterType        KubernetesClusterType `json:"cluster_type"`
+	RegionId           string                `json:"region_id"`
+	State              ClusterState          `json:"state"`
+	VpcId              string                `json:"vpc_id"`
+	VSwitchId          string                `json:"vswitch_id"`
+	SecurityGroupId    string                `json:"security_group_id"`
+	Tags               []Tag                 `json:"tags"`
+	Created            time.Time             `json:"created"`
+	Updated            time.Time             `json:"updated"`
+	InitVersion        string                `json:"init_version"`
+	CurrentVersion     string                `json:"current_version"`
+	PrivateZone        bool                  `json:"private_zone"`
+	DeletionProtection bool                  `json:"deletion_protection"`
+	EnableRRSA         bool                  `json:"enable_rrsa"`
+	ResourceGroupId    string                `json:"resource_group_id"`
+	ClusterSpec        string                `json:"cluster_spec"`
+	Profile            string                `json:"profile"`
+	MetaData           string                `json:"meta_data"`
+}
+
+type Tag struct {
+	Key   string `json:"key"`
+	Value string `json:"value"`
+}
+
+func (this *ServerlessCreationArgs) Validate() error {
+	if this.Name == "" || this.RegionId == "" || this.VpcId == "" {
+		return common.GetCustomError("InvalidParameters", "The name,region_id,vpc_id not allowed empty")
+	}
+	return nil
+}
+
+//create Serverless cluster
+func (client *Client) CreateServerlessKubernetesCluster(args *ServerlessCreationArgs) (*ClusterCommonResponse, error) {
+	if args == nil {
+		return nil, common.GetCustomError("InvalidArgs", "The args is nil ")
+	}
+
+	if err := args.Validate(); err != nil {
+		return nil, err
+	}
+
+	cluster := &ClusterCommonResponse{}
+	path := "/clusters"
+	if args.ResourceGroupId != "" {
+		// 创建集群到指定资源组
+		path = fmt.Sprintf("/resource_groups/%s/clusters", args.ResourceGroupId)
+	}
+	err := client.Invoke(common.Region(args.RegionId), http.MethodPost, path, nil, args, &cluster)
+	if err != nil {
+		return nil, err
+	}
+	return cluster, nil
+}
+
+//describe Serverless cluster
+func (client *Client) DescribeServerlessKubernetesCluster(clusterId string) (*ServerlessClusterResponse, error) {
+	cluster := &ServerlessClusterResponse{}
+	err := client.Invoke("", http.MethodGet, "/clusters/"+clusterId, nil, nil, cluster)
+	if err != nil {
+		return nil, err
+	}
+	return cluster, nil
+}
+
+//describe Serverless clsuters
+func (client *Client) DescribeServerlessKubernetesClusters() ([]*ServerlessClusterResponse, error) {
+	allClusters := make([]*ServerlessClusterResponse, 0)
+	askClusters := make([]*ServerlessClusterResponse, 0)
+
+	err := client.Invoke("", http.MethodGet, "/clusters", nil, nil, &allClusters)
+	if err != nil {
+		return askClusters, err
+	}
+
+	for _, cluster := range allClusters {
+		// Ask 1.0/2.0
+		if cluster.ClusterType == ClusterTypeServerlessKubernetes {
+			askClusters = append(askClusters, cluster)
+		}
+		// Ask 3.0
+		if cluster.ClusterType == ClusterTypeManagedKubernetes && cluster.Profile == ProfileServerlessKubernetes {
+			askClusters = append(askClusters, cluster)
+		}
+	}
+
+	return askClusters, nil
+}
+
+//new api for get cluster kube user config
+func (client *Client) DescribeClusterUserConfig(clusterId string, privateIpAddress bool) (*ClusterConfig, error) {
+	config := &ClusterConfig{}
+	query := url.Values{}
+	query.Add("PrivateIpAddress", strconv.FormatBool(privateIpAddress))
+
+	err := client.Invoke("", http.MethodGet, "/k8s/"+clusterId+"/user_config", query, nil, &config)
+	if err != nil {
+		return nil, err
+	}
+	return config, nil
+}
diff --git a/cs/serverless_test.go b/cs/serverless_test.go
new file mode 100644
index 0000000..738dacc
--- /dev/null
+++ b/cs/serverless_test.go
@@ -0,0 +1,96 @@
+package cs
+
+import (
+	"fmt"
+	"testing"
+	"time"
+)
+
+func Test_CreateServerlessKubernetesCluster(t *testing.T) {
+	client := NewTestClientForDebug()
+
+	tags := make([]Tag, 0)
+	tags = append(tags, Tag{
+		Key:   "key-a",
+		Value: "key-a",
+	})
+
+	tags = append(tags, Tag{
+		Key:   "key-b",
+		Value: "key-b",
+	})
+
+	tags = append(tags, Tag{
+		Key:   "key-c",
+		Value: "key-c",
+	})
+
+	args := &ServerlessCreationArgs{
+		ClusterType:          ClusterTypeManagedKubernetes,
+		Profile:              ProfileServerlessKubernetes,
+		Name:                 fmt.Sprintf("Serverless-cluster-%d", time.Now().Unix()),
+		RegionId:             string(TestRegionID),
+		VpcId:                TestVpcId,
+		VswitchIds:           []string{TestVSwitchId},
+		ServiceCIDR:          TestServiceCIDR,
+		LoggingType:          TestLoggingType,
+		PrivateZone:          true,
+		EndpointPublicAccess: true,
+		NatGateway:           true,
+		DeletionProtection:   true,
+		Tags:                 tags,
+	}
+
+	response, err := client.CreateServerlessKubernetesCluster(args)
+	if err != nil {
+		t.Errorf("Error %++v", err)
+	} else {
+		t.Logf("Response = %++v", response)
+	}
+}
+
+func Test_DescribeCluster(t *testing.T) {
+	client := NewTestClientForDebug()
+
+	cluster, err := client.DescribeServerlessKubernetesCluster(TestClusterId)
+	if err != nil {
+		t.Errorf("Error = %++v", err)
+	} else {
+		t.Logf("Cluster = %#v", cluster)
+		t.Logf("%v ", cluster.Created)
+	}
+}
+
+func Test_DescribeClusterUserConfig(t *testing.T) {
+	client := NewTestClientForDebug()
+
+	config, err := client.DescribeClusterUserConfig(TestClusterId, TestPrivateIpAddress)
+	if err != nil {
+		t.Errorf("Error = %++v", err)
+	} else {
+		t.Logf("Config = %#v", config)
+	}
+}
+
+func Test_DeleteServerlessCluster(t *testing.T) {
+	client := NewTestClientForDebug()
+
+	err := client.DeleteCluster(TestClusterId)
+	if err != nil {
+		t.Errorf("Error = %++v", err)
+	} else {
+		t.Logf("OK")
+	}
+}
+
+func Test_DescribeServerlessKubernetesClusters(t *testing.T) {
+	client := NewTestClientForDebug()
+	clusters, err := client.DescribeServerlessKubernetesClusters()
+	if err != nil {
+		t.Errorf("Error = %++v", err)
+	} else {
+		for _, cluster := range clusters {
+			t.Logf("Cluster = %#v", cluster)
+		}
+	}
+}
diff --git a/cs/token.go b/cs/token.go
new file mode 100644
index 0000000..aba207e
--- /dev/null
+++ b/cs/token.go
@@ -0,0 +1,49 @@
+package cs
+
+import (
+	"fmt"
+	"github.com/denverdino/aliyungo/common"
+	"net/http"
+)
+
+type ClusterTokenReqeust struct {
+	//token 过期时间,如果不填写,则默认24小时过期
+	Expired       int64 `json:"expired"`
+	IsPermanently bool  `json:"is_permanently"`
+}
+
+type ClusterTokenResponse struct {
+	Created int64 ` json:"created"`
+	Updated int64 `json:"updated"`
+	Expired int64 ` json:"expired"`
+
+	ClusterID string ` json:"cluster_id"`
+
+	Token    string ` json:"token"`
+	IsActive int    ` json:"is_active"`
+}
+
+func (client *Client) CreateClusterToken(clusterId string, request *ClusterTokenReqeust) (*ClusterTokenResponse, error) {
+	response := &ClusterTokenResponse{}
+	err := client.Invoke("", http.MethodPost, "/clusters/"+clusterId+"/token", nil, request, response)
+	return response, err
+}
+
+func (client *Client) RevokeToken(token string) error {
+	return client.Invoke("", http.MethodDelete, "/token/"+token+"/revoke", nil, nil, nil)
+}
+
+func (client *Client) DescribeClusterTokens(clusterId string) ([]*ClusterTokenResponse, error) {
+	response := make([]*ClusterTokenResponse, 0)
+	err := client.Invoke("", http.MethodGet, "/clusters/"+clusterId+"/tokens", nil, nil, &response)
+	return response, err
+}
+
+func (client *Client) DescribeClusterToken(clusterId, token string) (*ClusterTokenResponse, error) {
+	if clusterId == "" || token == "" {
+		return nil, common.GetCustomError("InvalidParamter", "The clusterId or token is empty")
+	}
+	tokenInfo := &ClusterTokenResponse{}
+	err := client.Invoke("", http.MethodGet, fmt.Sprintf("/clusters/%s/tokens/%s", clusterId, token), nil, nil, tokenInfo)
+	return tokenInfo, err
+}
diff --git a/cs/token_test.go b/cs/token_test.go
new file mode 100644
index 0000000..13d9681
--- /dev/null
+++ b/cs/token_test.go
@@ -0,0 +1,73 @@
+package cs
+
+import (
+	"testing"
+	"time"
+)
+
+func Test_CreateClusterToken(t *testing.T) {
+	client := NewTestClientForDebug()
+
+	req := &ClusterTokenReqeust{
+		Expired:       time.Now().Unix() + 86400,
+		IsPermanently: false,
+	}
+
+	token, err := client.CreateClusterToken(TestClusterId, req)
+	if err != nil {
+		t.Fatalf("Error %++v", err)
+	} else {
+		t.Logf("Token = %++v", token)
+	}
+}
+
+func Test_RevokeClusterToken(t *testing.T) {
+	client := NewTestClientForDebug()
+	req := &ClusterTokenReqeust{
+		Expired:       time.Now().Unix() + 86400,
+		IsPermanently: false,
+	}
+
+	token, err := client.CreateClusterToken(TestClusterId, req)
+	if err != nil {
+		t.Fatalf("Error = %++v", err)
+	} else {
+		err = client.RevokeToken(token.Token)
+		if err != nil {
+			t.Fatalf("Error = %++v", err)
+		} else {
+			tokens, err := client.DescribeClusterTokens(TestClusterId)
+			if err != nil {
+				t.Fatalf("Error %++v", err)
+			} else {
+				for _, token := range tokens {
+					t.Logf("Token = %++v", token)
+				}
+			}
+		}
+	}
+}
+
+func Test_DescribeClusterTokens(t *testing.T) {
+	client := NewTestClientForDebug()
+
+	tokens, err := client.DescribeClusterTokens(TestClusterId)
+	if err != nil {
+		t.Fatalf("Error %++v", err)
+	} else {
+		for _, token := range tokens {
+			t.Logf("Token = %++v", token)
+		}
+	}
+}
+
+func Test_DescribeClusterToken(t *testing.T) {
+	client := NewTestClientForDebug()
+
+	token, err := client.DescribeClusterToken(TestClusterId, TestToken)
+	if err != nil {
+		t.Fatalf("Error %++v", err)
+	} else {
+		t.Logf("Token = %++v", token)
+	}
+}
diff --git a/debian/changelog b/debian/changelog
index af504dd..3c5da63 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+golang-github-denverdino-aliyungo (0.0~git20220905.589a058-1) UNRELEASED; urgency=low
+
+  * New upstream snapshot.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Thu, 08 Sep 2022 12:01:29 -0000
+
 golang-github-denverdino-aliyungo (0.0~git20180921.13fa8aa-3) unstable; urgency=medium
 
   [ Debian Janitor ]
diff --git a/ecs/client.go b/ecs/client.go
index 7ae1fb5..0beadcd 100644
--- a/ecs/client.go
+++ b/ecs/client.go
@@ -64,6 +64,16 @@ func NewECSClientWithSecurityToken(accessKeyId string, accessKeySecret string, s
 	return NewECSClientWithEndpointAndSecurityToken(endpoint, accessKeyId, accessKeySecret, securityToken, regionID)
 }
 
+//only for Hangzhou Regional Domain
+func NewECSClientWithSecurityToken4RegionalDomain(accessKeyId string, accessKeySecret string, securityToken string, regionID common.Region) *Client {
+	endpoint := os.Getenv("ECS_ENDPOINT")
+	if endpoint != "" {
+		return NewECSClientWithSecurityToken(accessKeyId, accessKeySecret, securityToken, regionID)
+	}
+
+	return NewECSClientWithEndpointAndSecurityToken4RegionalDomain(endpoint, accessKeyId, accessKeySecret, securityToken, regionID)
+}
+
 func NewECSClientWithEndpoint(endpoint string, accessKeyId string, accessKeySecret string, regionID common.Region) *Client {
 	return NewECSClientWithEndpointAndSecurityToken(endpoint, accessKeyId, accessKeySecret, "", regionID)
 }
@@ -81,6 +91,19 @@ func NewECSClientWithEndpointAndSecurityToken(endpoint string, accessKeyId strin
 	return client
 }
 
+func NewECSClientWithEndpointAndSecurityToken4RegionalDomain(endpoint string, accessKeyId string, accessKeySecret string, securityToken string, regionID common.Region) *Client {
+	client := &Client{}
+	client.WithEndpoint(endpoint).
+		WithVersion(ECSAPIVersion).
+		WithAccessKeyId(accessKeyId).
+		WithAccessKeySecret(accessKeySecret).
+		WithSecurityToken(securityToken).
+		WithServiceCode(ECSServiceCode).
+		WithRegionID(regionID).
+		InitClient4RegionalDomain()
+	return client
+}
+
 // ---------------------------------------
 // NewVPCClient creates a new instance of VPC client
 // ---------------------------------------
@@ -97,6 +120,16 @@ func NewVPCClientWithSecurityToken(accessKeyId string, accessKeySecret string, s
 	return NewVPCClientWithEndpointAndSecurityToken(endpoint, accessKeyId, accessKeySecret, securityToken, regionID)
 }
 
+//Only for Hangzhou
+func NewVPCClientWithSecurityToken4RegionalDomain(accessKeyId string, accessKeySecret string, securityToken string, regionID common.Region) *Client {
+	endpoint := os.Getenv("VPC_ENDPOINT")
+	if endpoint != "" {
+		return NewVPCClientWithEndpointAndSecurityToken(endpoint, accessKeyId, accessKeySecret, securityToken, regionID)
+	}
+
+	return NewVPCClientWithEndpointAndSecurityToken4RegionalDomain(endpoint, accessKeyId, accessKeySecret, securityToken, regionID)
+}
+
 func NewVPCClientWithEndpoint(endpoint string, accessKeyId string, accessKeySecret string, regionID common.Region) *Client {
 	return NewVPCClientWithEndpointAndSecurityToken(endpoint, accessKeyId, accessKeySecret, "", regionID)
 }
@@ -114,6 +147,20 @@ func NewVPCClientWithEndpointAndSecurityToken(endpoint string, accessKeyId strin
 	return client
 }
 
+//Only for Hangzhou
+func NewVPCClientWithEndpointAndSecurityToken4RegionalDomain(endpoint string, accessKeyId string, accessKeySecret string, securityToken string, regionID common.Region) *Client {
+	client := &Client{}
+	client.WithEndpoint(endpoint).
+		WithVersion(VPCAPIVersion).
+		WithAccessKeyId(accessKeyId).
+		WithAccessKeySecret(accessKeySecret).
+		WithSecurityToken(securityToken).
+		WithServiceCode(VPCServiceCode).
+		WithRegionID(regionID).
+		InitClient4RegionalDomain()
+	return client
+}
+
 // ---------------------------------------
 // NewVPCClientWithRegion creates a new instance of VPC client automatically get endpoint
 // ---------------------------------------
diff --git a/ecs/client_test.go b/ecs/client_test.go
index 629ee94..4163eb1 100644
--- a/ecs/client_test.go
+++ b/ecs/client_test.go
@@ -1,6 +1,9 @@
 package ecs
 
 import (
+	"github.com/denverdino/aliyungo/common"
+	"github.com/magiconair/properties/assert"
+	"os"
 	"testing"
 )
 
@@ -73,3 +76,12 @@ func TestECSDescribe(t *testing.T) {
 		}
 	}
 }
+
+func Test_NewVPCClientWithSecurityToken4RegionalDomain(t *testing.T) {
+	client := NewVPCClientWithSecurityToken4RegionalDomain(TestAccessKeyId, TestAccessKeySecret, TestSecurityToken, common.Beijing)
+	assert.Equal(t, client.GetEndpoint(), "https://vpc-vpc.cn-beijing.aliyuncs.com")
+
+	os.Setenv("VPC_ENDPOINT", "vpc.aliyuncs.com")
+	client = NewVPCClientWithSecurityToken4RegionalDomain(TestAccessKeyId, TestAccessKeySecret, TestSecurityToken, common.Beijing)
+	assert.Equal(t, client.GetEndpoint(), "vpc.aliyuncs.com")
+}
diff --git a/ecs/config_test.go b/ecs/config_test.go
index 41d430f..fcd7ea0 100644
--- a/ecs/config_test.go
+++ b/ecs/config_test.go
@@ -14,12 +14,14 @@ var (
 	TestSecurityToken   = os.Getenv("SecurityToken")
 	TestRegionID        = common.Region(os.Getenv("RegionId"))
 	TestVpcId           = os.Getenv("VpcId")
+	TestVswitchID       = os.Getenv("TestVswitchID")
 
 	TestInstanceId      = os.Getenv("InstanceId")
-	TestSecurityGroupId = "MY_TEST_SECURITY_GROUP_ID"
-	TestImageId         = "MY_IMAGE_ID"
+	TestSecurityGroupId = os.Getenv("TestSecurityGroupId")
+	TestResourceGroupId = os.Getenv("TestResourceGroupId")
+	TestImageId         = os.Getenv("TestImageId")
 	TestAccountId       = "MY_TEST_ACCOUNT_ID" //Get from https://account.console.aliyun.com
-	TestInstanceType    = "ecs.n4.large"
+	TestInstanceType    = os.Getenv("InstanceType")
 	TestVSwitchID       = "MY_TEST_VSWITCHID"
 
 	TestIAmRich = false
@@ -59,7 +61,7 @@ var testLocationClient *Client
 
 func NetTestLocationClientForDebug() *Client {
 	if testLocationClient == nil {
-		testLocationClient = NewECSClient(TestAccessKeyId, TestAccessKeySecret, TestRegionID)
+		testLocationClient = NewECSClientWithSecurityToken4RegionalDomain(TestAccessKeyId, TestAccessKeySecret, TestSecurityToken, TestRegionID)
 		testLocationClient.SetDebug(true)
 	}
 
diff --git a/ecs/disks.go b/ecs/disks.go
index ae2c408..2f5f31e 100644
--- a/ecs/disks.go
+++ b/ecs/disks.go
@@ -26,6 +26,17 @@ const (
 	DiskCategoryEphemeralSSD    = DiskCategory("ephemeral_ssd")
 	DiskCategoryCloudEfficiency = DiskCategory("cloud_efficiency")
 	DiskCategoryCloudSSD        = DiskCategory("cloud_ssd")
+	DiskCategoryCloudESSD       = DiskCategory("cloud_essd")
+)
+
+// Performance Level of disks
+type DiskPerformanceLevel string
+
+const (
+	DiskPL0 = DiskCategory("PL0")
+	DiskPL1 = DiskCategory("PL1")
+	DiskPL2 = DiskCategory("PL2")
+	DiskPL3 = DiskCategory("PL3")
 )
 
 // Status of disks
@@ -96,6 +107,8 @@ type DiskItemType struct {
 	AttachedTime       util.ISO6801Time
 	DetachedTime       util.ISO6801Time
 	DiskChargeType     DiskChargeType
+	StorageSetId       string
+	PerformanceLevel   DiskPerformanceLevel
 }
 
 type DescribeDisksResponse struct {
@@ -139,8 +152,13 @@ type CreateDiskArgs struct {
 	Encrypted    bool
 	DiskCategory DiskCategory
 	Size         int
+	Tag          map[string]string
 	SnapshotId   string
 	ClientToken  string
+	KMSKeyID     string
+	StorageSetId string
+
+	PerformanceLevel DiskPerformanceLevel
 }
 
 type CreateDisksResponse struct {
@@ -347,7 +365,7 @@ func (client *Client) WaitForDisk(regionId common.Region, diskId string, status
 		if err != nil {
 			return err
 		}
-		if disks == nil || len(disks) == 0 {
+		if len(disks) == 0 {
 			return common.GetClientErrorFromString("Not found")
 		}
 		if disks[0].Status == status {
diff --git a/ecs/eni.go b/ecs/eni.go
index a3d9a4c..b8949b7 100644
--- a/ecs/eni.go
+++ b/ecs/eni.go
@@ -5,16 +5,22 @@ import (
 	"time"
 
 	"github.com/denverdino/aliyungo/common"
+	"github.com/denverdino/aliyungo/util"
 )
 
 type CreateNetworkInterfaceArgs struct {
-	RegionId             common.Region
-	VSwitchId            string
-	PrimaryIpAddress     string // optional
-	SecurityGroupId      string
-	NetworkInterfaceName string // optional
-	Description          string // optional
-	ClientToken          string // optional
+	RegionId                       common.Region
+	VSwitchId                      string
+	PrimaryIpAddress               string // optional
+	SecurityGroupId                string
+	NetworkInterfaceName           string            // optional
+	Description                    string            // optional
+	ClientToken                    string            // optional
+	Tag                            map[string]string // optional
+	ResourceGroupId                string            // optional
+	SecurityGroupIds               []string          `query:"list"` // optional
+	PrivateIpAddress               []string          `query:"list"` // optional
+	SecondaryPrivateIpAddressCount int
 }
 
 type CreateNetworkInterfaceResponse struct {
@@ -33,7 +39,9 @@ type DeleteNetworkInterfaceResponse struct {
 type DescribeNetworkInterfacesArgs struct {
 	RegionId             common.Region
 	VSwitchId            string
+	VpcID                string
 	PrimaryIpAddress     string
+	PrivateIpAddress     []string `query:"list"`
 	SecurityGroupId      string
 	NetworkInterfaceName string
 	Type                 string
@@ -46,9 +54,34 @@ type NetworkInterfaceType struct {
 	NetworkInterfaceId   string
 	NetworkInterfaceName string
 	PrimaryIpAddress     string
-	MacAddress           string
-	Status               string
-	PrivateIpAddress     string
+	PrivateIpSets        struct {
+		PrivateIpSet []PrivateIpType
+	}
+	MacAddress         string
+	Status             string
+	Type               string
+	VpcId              string
+	VSwitchId          string
+	ZoneId             string
+	AssociatedPublicIp AssociatedPublicIp
+	SecurityGroupIds   struct {
+		SecurityGroupId []string
+	}
+	Description      string
+	InstanceId       string
+	CreationTime     util.ISO6801Time
+	PrivateIpAddress string
+}
+
+type PrivateIpType struct {
+	PrivateIpAddress   string
+	Primary            bool
+	AssociatedPublicIp AssociatedPublicIp
+}
+
+type AssociatedPublicIp struct {
+	PublicIpAddress string
+	AllocationId    string
 }
 
 type DescribeNetworkInterfacesResponse struct {
@@ -75,12 +108,38 @@ type DetachNetworkInterfaceResponse common.Response
 type ModifyNetworkInterfaceAttributeArgs struct {
 	RegionId             common.Region
 	NetworkInterfaceId   string
-	SecurityGroupId      []string
+	SecurityGroupId      []string `query:"list"`
 	NetworkInterfaceName string
 	Description          string
 }
 type ModifyNetworkInterfaceAttributeResponse common.Response
 
+type UnassignPrivateIpAddressesArgs struct {
+	RegionId           common.Region
+	NetworkInterfaceId string
+	PrivateIpAddress   []string `query:"list"`
+}
+
+type UnassignPrivateIpAddressesResponse common.Response
+
+type AssignPrivateIpAddressesArgs struct {
+	RegionId                       common.Region
+	NetworkInterfaceId             string
+	PrivateIpAddress               []string `query:"list"` // optional
+	SecondaryPrivateIpAddressCount int      // optional
+}
+
+type AssignPrivateIpAddressesResponse struct {
+	common.Response
+
+	AssignedPrivateIpAddressesSet struct {
+		NetworkInterfaceId string
+		PrivateIpSet       struct {
+			PrivateIpAddress []string
+		}
+	}
+}
+
 func (client *Client) CreateNetworkInterface(args *CreateNetworkInterfaceArgs) (resp *CreateNetworkInterfaceResponse, err error) {
 	resp = &CreateNetworkInterfaceResponse{}
 	err = client.Invoke("CreateNetworkInterface", args, resp)
@@ -117,6 +176,18 @@ func (client *Client) ModifyNetworkInterfaceAttribute(args *ModifyNetworkInterfa
 	return resp, err
 }
 
+func (client *Client) UnassignPrivateIpAddresses(args *UnassignPrivateIpAddressesArgs) (resp *UnassignPrivateIpAddressesResponse, err error) {
+	resp = &UnassignPrivateIpAddressesResponse{}
+	err = client.Invoke("UnassignPrivateIpAddresses", args, resp)
+	return resp, err
+}
+
+func (client *Client) AssignPrivateIpAddresses(args *AssignPrivateIpAddressesArgs) (resp *AssignPrivateIpAddressesResponse, err error) {
+	resp = &AssignPrivateIpAddressesResponse{}
+	err = client.Invoke("AssignPrivateIpAddresses", args, resp)
+	return resp, err
+}
+
 // Default timeout value for WaitForInstance method
 const NetworkInterfacesDefaultTimeout = 120
 
diff --git a/ecs/eni_test.go b/ecs/eni_test.go
new file mode 100644
index 0000000..cd6290f
--- /dev/null
+++ b/ecs/eni_test.go
@@ -0,0 +1,109 @@
+package ecs
+
+import (
+	"github.com/denverdino/aliyungo/common"
+	"testing"
+	"time"
+)
+
+func TestAssignPrivateIPAddresses(t *testing.T) {
+	req := AssignPrivateIpAddressesArgs{
+		RegionId:           common.Beijing,
+		NetworkInterfaceId: "eni-testeni",
+		PrivateIpAddress:   []string{"192.168.1.200", "192.168.1.201"},
+	}
+	client := NewTestClient()
+	assignPrivateIpAddressesResponse, err := client.AssignPrivateIpAddresses(&req)
+	if err != nil {
+		t.Errorf("Failed to AssignPrivateIpAddresses: %v", err)
+	}
+
+	if assignPrivateIpAddressesResponse.AssignedPrivateIpAddressesSet.NetworkInterfaceId != "eni-testeni" {
+		t.Errorf("assert network id mismatch.%s\n", assignPrivateIpAddressesResponse.AssignedPrivateIpAddressesSet.NetworkInterfaceId)
+	}
+
+	time.Sleep(5 * time.Second)
+
+	req = AssignPrivateIpAddressesArgs{
+		RegionId:                       common.Beijing,
+		NetworkInterfaceId:             "eni-testeni",
+		SecondaryPrivateIpAddressCount: 1,
+	}
+
+	_, err = client.AssignPrivateIpAddresses(&req)
+	if err != nil {
+		t.Errorf("Failed to AssignPrivateIpAddresses: %v", err)
+	}
+}
+
+func TestDescribeENI(t *testing.T) {
+	req := DescribeNetworkInterfacesArgs{
+		RegionId:           common.Beijing,
+		NetworkInterfaceId: []string{"eni-testeni"},
+	}
+	client := NewTestClient()
+	resp, err := client.DescribeNetworkInterfaces(&req)
+	if err != nil {
+		t.Errorf("Failed to DescribeNetworkInterfaces: %v", err)
+	}
+	if len(resp.NetworkInterfaceSets.NetworkInterfaceSet[0].PrivateIpSets.PrivateIpSet) != 4 {
+		t.Errorf("assert network private ip count be 4, %+v", resp.NetworkInterfaceSets.NetworkInterfaceSet[0].PrivateIpSets.PrivateIpSet)
+	}
+	t.Logf("%+v", resp.NetworkInterfaceSets.NetworkInterfaceSet[0])
+}
+
+func TestFindENIByPrivateIP(t *testing.T) {
+	req := DescribeNetworkInterfacesArgs{
+		RegionId:         common.Shanghai,
+		VpcID:            "vpc-xxx",
+		PrivateIpAddress: []string{"192.168.108.191"},
+	}
+	client := NewTestClient()
+	resp, err := client.DescribeNetworkInterfaces(&req)
+	if err != nil {
+		t.Errorf("Failed to DescribeNetworkInterfaces: %v", err)
+	}
+	t.Logf("%+v", resp.NetworkInterfaceSets.NetworkInterfaceSet)
+}
+
+func TestUnAssignPrivateIPAddresses(t *testing.T) {
+	req := UnassignPrivateIpAddressesArgs{
+		RegionId:           common.Beijing,
+		NetworkInterfaceId: "eni-testeni",
+		PrivateIpAddress:   []string{"192.168.1.200", "192.168.1.201"},
+	}
+	client := NewTestClient()
+	_, err := client.UnassignPrivateIpAddresses(&req)
+	if err != nil {
+		t.Errorf("Failed to UnAssignPrivateIpAddresses: %v", err)
+	}
+}
+
+func TestModifyNetworkInterfaceAttribute(t *testing.T) {
+	args := &ModifyNetworkInterfaceAttributeArgs{
+		RegionId:           common.Shanghai,
+		NetworkInterfaceId: "eni-testeni",
+		SecurityGroupId:    []string{"sg-xxx", "sg-yyy"},
+	}
+
+	client := NewTestClient()
+	_, err := client.ModifyNetworkInterfaceAttribute(args)
+	if err != nil {
+		t.Errorf("failed to ModifyNetworkInterfaceAttribute: %v", err)
+	}
+}
+
+func TestCreateNetworkInterface(t *testing.T) {
+	args := &CreateNetworkInterfaceArgs{
+		RegionId:                       common.Shanghai,
+		VSwitchId:                      "vsw-xxx",
+		SecurityGroupIds:               []string{"sg-xxx", "sg-yyy"},
+		SecondaryPrivateIpAddressCount: 9,
+	}
+	client := NewTestClient()
+	resp, err := client.CreateNetworkInterface(args)
+	if err != nil {
+		t.Errorf("failed to CreateNetworkInterface: %v", err)
+	}
+	t.Logf("new eni info: %+v", resp)
+}
diff --git a/ecs/images.go b/ecs/images.go
index e50297a..79c0f75 100644
--- a/ecs/images.go
+++ b/ecs/images.go
@@ -313,7 +313,7 @@ func (client *Client) WaitForImageReady(regionId common.Region, imageId string,
 		if err != nil {
 			return err
 		}
-		if images == nil || len(images) == 0 {
+		if len(images) == 0 {
 			args.Status = ImageStatusAvailable
 			images, _, er := client.DescribeImages(&args)
 			if er == nil && len(images) == 1 {
diff --git a/ecs/instance_types.go b/ecs/instance_types.go
index 9b71081..04af1a8 100644
--- a/ecs/instance_types.go
+++ b/ecs/instance_types.go
@@ -4,23 +4,25 @@ import "github.com/denverdino/aliyungo/common"
 
 type DescribeInstanceTypesArgs struct {
 	InstanceTypeFamily string
+	InstanceTypes      []string `query:"list"`
 }
 
 //
 // You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&instancetypeitemtype
 type InstanceTypeItemType struct {
-	InstanceTypeId       string
-	CpuCoreCount         int
-	MemorySize           float64
-	InstanceTypeFamily   string
-	GPUAmount            int
-	GPUSpec              string
-	InitialCredit        int
-	BaselineCredit       int
-	EniQuantity          int
-	LocalStorageCapacity int
-	LocalStorageAmount   int
-	LocalStorageCategory string
+	InstanceTypeId              string
+	CpuCoreCount                int
+	MemorySize                  float64
+	InstanceTypeFamily          string
+	GPUAmount                   int
+	GPUSpec                     string
+	InitialCredit               int
+	BaselineCredit              int
+	EniQuantity                 int
+	EniPrivateIpAddressQuantity int
+	LocalStorageCapacity        int
+	LocalStorageAmount          int
+	LocalStorageCategory        string
 }
 
 type DescribeInstanceTypesResponse struct {
diff --git a/ecs/instance_types_test.go b/ecs/instance_types_test.go
index 7aad567..70a0a0c 100644
--- a/ecs/instance_types_test.go
+++ b/ecs/instance_types_test.go
@@ -5,7 +5,6 @@ import (
 )
 
 func TestDescribeInstanceTypes(t *testing.T) {
-
 	client := NewTestClient()
 	instanceTypes, err := client.DescribeInstanceTypes()
 	if err != nil {
@@ -14,4 +13,14 @@ func TestDescribeInstanceTypes(t *testing.T) {
 	for _, instanceType := range instanceTypes {
 		t.Logf("InstanceType: %++v", instanceType)
 	}
+
+	instanceTypes, err = client.DescribeInstanceTypesNew(&DescribeInstanceTypesArgs{
+		InstanceTypes: []string{"ecs.ec5.24xlarge", "ecs.ddh6s.custom.c4m48"},
+	})
+	if err != nil {
+		t.Fatalf("Failed to DescribeInstanceTypesNew: %v", err)
+	}
+	for _, instanceType := range instanceTypes {
+		t.Logf("InstanceType: %++v", instanceType)
+	}
 }
diff --git a/ecs/instances.go b/ecs/instances.go
index dd424f9..f0bac0b 100644
--- a/ecs/instances.go
+++ b/ecs/instances.go
@@ -230,13 +230,15 @@ const (
 //
 // You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&instanceattributestype
 type InstanceAttributesType struct {
+	AutoReleaseTime    string
 	InstanceId         string
 	InstanceName       string
 	Description        string
 	ImageId            string
 	RegionId           common.Region
 	ZoneId             string
-	CPU                int
+	Cpu                int
+	CpuOptions         CpuOptionsType
 	Memory             int
 	ClusterId          string
 	InstanceType       string
@@ -248,26 +250,114 @@ type InstanceAttributesType struct {
 	SecurityGroupIds   struct {
 		SecurityGroupId []string
 	}
-	PublicIpAddress         IpAddressSetType
-	InnerIpAddress          IpAddressSetType
-	InstanceNetworkType     string //enum Classic | Vpc
-	InternetMaxBandwidthIn  int
-	InternetMaxBandwidthOut int
-	InternetChargeType      common.InternetChargeType
-	CreationTime            util.ISO6801Time //time.Time
-	VpcAttributes           VpcAttributesType
-	EipAddress              EipAddressAssociateType
-	IoOptimized             StringOrBool
-	InstanceChargeType      common.InstanceChargeType
-	ExpiredTime             util.ISO6801Time
-	Tags                    struct {
+	PublicIpAddress            IpAddressSetType
+	InnerIpAddress             IpAddressSetType
+	InstanceNetworkType        string //enum Classic | Vpc
+	InternetMaxBandwidthIn     int
+	InternetMaxBandwidthOut    int
+	InternetChargeType         common.InternetChargeType
+	CreationTime               util.ISO6801Time //time.Time
+	CreditSpecification        string
+	DedicatedHostAttribute     DedicatedHostAttributeType
+	DedicatedInstanceAttribute DedicatedInstanceAttributeType
+	DeletionProtection         bool
+	DeploymentSetGroupNo       int
+	DeploymentSetId            string
+	DeviceAvailable            bool
+	EcsCapacityReservationAttr EcsCapacityReservationAttrType
+	VpcAttributes              VpcAttributesType
+	EipAddress                 EipAddressAssociateType
+	IoOptimized                StringOrBool
+	InstanceChargeType         common.InstanceChargeType
+	ExpiredTime                util.ISO6801Time
+	GPUAmount                  int
+	GPUSpec                    string
+	HibernationOptions         HibernationOptionsType
+	HpcClusterId               string
+	ISP                        string
+	LocalStorageAmount         int
+	LocalStorageCapacity       int
+	MetadataOptions            MetadataOptionsType
+	NetworkInterfaces          struct {
+		NetworkInterface []InstanceNetworkInterfaceType
+	}
+	OSName          string
+	OSNameEn        string
+	OSType          string
+	RdmaIpAddress   RdmaIpAddressType
+	Recyclable      bool
+	ResourceGroupId string
+	SaleCycle       string
+	SpotDuration    int
+	StartTime       string
+	StoppedMode     string
+	Tags            struct {
 		Tag []TagItemType
 	}
+	VlanId         string
 	SpotStrategy   SpotStrategyType
 	SpotPriceLimit float64
 	KeyPairName    string
 }
 
+type CpuOptionsType struct {
+	CoreCount      int
+	Numa           string
+	ThreadsPerCore int
+}
+
+type DedicatedHostAttributeType struct {
+	DedicatedHostClusterId string
+	DedicatedHostId        string
+	DedicatedHostName      string
+}
+
+type DedicatedInstanceAttributeType struct {
+	Affinity string
+	Tenancy  string
+}
+
+type EcsCapacityReservationAttrType struct {
+	CapacityReservationId         string
+	CapacityReservationPreference string
+}
+
+type HibernationOptionsType struct {
+	Configured bool
+}
+
+type MetadataOptionsType struct {
+	HttpEndpoint            string
+	HttpPutResponseHopLimit int
+	HttpTokens              string
+}
+
+type InstanceNetworkInterfaceType struct {
+	Ipv6Sets struct {
+		Ipv6Set []Ipv6SetType
+	}
+	MacAddress         string
+	NetworkInterfaceId string
+	PrimaryIpAddress   string
+	Type               string
+	PrivateIpSets      struct {
+		PrivateIpSet []PrivateIpSetType
+	}
+}
+
+type Ipv6SetType struct {
+	Ipv6Address string
+}
+
+type PrivateIpSetType struct {
+	Primary          bool
+	PrivateIpAddress string
+}
+
+type RdmaIpAddressType struct {
+	RdmaIpAddress []string
+}
+
 type DescribeInstanceAttributeResponse struct {
 	common.Response
 	InstanceAttributesType
@@ -401,16 +491,34 @@ type DescribeInstancesArgs struct {
 	PrivateIpAddresses  string
 	InnerIpAddresses    string
 	PublicIpAddresses   string
+	EipAddresses        string
+	InstanceChargeType  common.InstanceChargeType
+	InternetChargeType  common.InternetChargeType
+	ImageId             string
+	LockReason          string
 	SecurityGroupId     string
-	Tag                 map[string]string
 	InstanceType        string
 	SpotStrategy        SpotStrategyType
 	common.Pagination
+	NextToken          string
+	MaxResults         int
+	Filter             []Filter
+	IoOptimized        *bool
+	InstanceTypeFamily string
+	KeyPairName        string
+	ResourceGroupId    string
+	HpcClusterId       string
+	RdmaIpAddresses    string
+	DryRun             bool
+	HttpEndpoint       string
+	HttpTokens         string
+	Tag                []TagType
 }
 
 type DescribeInstancesResponse struct {
 	common.Response
 	common.PaginationResult
+	NextToken string
 	Instances struct {
 		Instance []InstanceAttributesType
 	}
@@ -488,13 +596,18 @@ type DataDiskType struct {
 	Description        string
 	Device             string
 	DeleteWithInstance bool
+	PerformanceLevel   string
+	KMSKeyId           string
+	Encrypted          bool
 }
 
 type SystemDiskType struct {
-	Size        int
-	Category    DiskCategory //Enum cloud, ephemeral, ephemeral_ssd
-	DiskName    string
-	Description string
+	Size                 int
+	Category             DiskCategory //Enum cloud, ephemeral, ephemeral_ssd
+	DiskName             string
+	Description          string
+	PerformanceLevel     DiskPerformanceLevel
+	AutoSnapshotPolicyId string
 }
 
 type IoOptimized string
@@ -545,6 +658,7 @@ type CreateInstanceArgs struct {
 	RegionId                    common.Region
 	ZoneId                      string
 	ImageId                     string
+	ImageFamily                 string
 	InstanceType                string
 	SecurityGroupId             string
 	InstanceName                string
@@ -554,7 +668,13 @@ type CreateInstanceArgs struct {
 	InternetMaxBandwidthOut     int
 	HostName                    string
 	Password                    string
+	PasswordInherit             bool
+	DeploymentSetId             string
+	ClusterId                   string
+	VlanId                      string
+	InnerIpAddress              string
 	IoOptimized                 IoOptimized
+	UseAdditionalService        *bool
 	SystemDisk                  SystemDiskType
 	DataDisk                    []DataDiskType
 	VSwitchId                   string
@@ -568,9 +688,35 @@ type CreateInstanceArgs struct {
 	AutoRenewPeriod             int
 	SpotStrategy                SpotStrategyType
 	SpotPriceLimit              float64
+	SpotDuration                *int
+	SpotInterruptionBehavior    string
 	KeyPairName                 string
 	RamRoleName                 string
 	SecurityEnhancementStrategy SecurityEnhancementStrategy
+	ResourceGroupId             string
+	HpcClusterId                string
+	DryRun                      bool
+	DedicatedHostId             string
+	CreditSpecification         string
+	DeletionProtection          bool
+	Affinity                    string
+	Tenancy                     string
+	StorageSetId                string
+	StorageSetPartitionNumber   int
+	HttpEndpoint                string
+	HttpTokens                  string
+	PrivatePoolOptions          []PrivatePoolOptionsType
+	Tag                         []TagType
+}
+
+type PrivatePoolOptionsType struct {
+	MatchCriteria string
+	Id            string
+}
+
+type TagType struct {
+	Key   string
+	Value string
 }
 
 type CreateInstanceResponse struct {
diff --git a/ecs/instances_test.go b/ecs/instances_test.go
index b67699c..7a1c682 100644
--- a/ecs/instances_test.go
+++ b/ecs/instances_test.go
@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"testing"
+	"time"
 
 	"github.com/denverdino/aliyungo/common"
 )
@@ -332,7 +333,7 @@ func TestClient_DescribeInstances(t *testing.T) {
 		RegionId: TestRegionID,
 		Pagination: common.Pagination{
 			PageNumber: 1,
-			PageSize:   100,
+			PageSize:   2,
 		},
 		//SecurityToken: TestSecurityToken,
 	}
@@ -346,3 +347,89 @@ func TestClient_DescribeInstances(t *testing.T) {
 		}
 	}
 }
+
+func Test_CreateDescribeAndDeletiongInstances(t *testing.T) {
+	if TestAccessKeyId == "" || TestAccessKeySecret == "" || TestVpcId == "" || TestVswitchID == "" {
+		t.Skip("missing env")
+		return
+	}
+	client := NewTestClientForDebug()
+
+	//createInstance
+	createInstanceArgs := &CreateInstanceArgs{
+		RegionId:           TestRegionID,
+		InstanceType:       TestInstanceType,
+		VSwitchId:          TestVswitchID,
+		ResourceGroupId:    TestResourceGroupId,
+		SecurityGroupId:    TestSecurityGroupId,
+		ImageId:            TestImageId,
+		InstanceChargeType: common.PostPaid,
+	}
+
+	instanceId, err := client.CreateInstance(createInstanceArgs)
+	if err != nil {
+		t.Fatalf("CreateInstance error %v", err)
+	}
+
+	//wait for stoppend
+	err = client.WaitForInstance(instanceId, Stopped, 60)
+	if err != nil {
+		t.Fatalf("WaitForInstance Stopped error %v", err)
+	}
+
+	// start Instance
+	err = client.StartInstance(instanceId)
+	if err != nil {
+		t.Fatalf("StartInstance %s error %v", instanceId, err)
+	}
+
+	//wait for running
+	err = client.WaitForInstance(instanceId, Running, 0)
+	if err != nil {
+		t.Fatalf("WaitForInstance Running error %v", err)
+	}
+
+	time.Sleep(10 * time.Second)
+	t.Logf("After 30s ,describe instance %s", instanceId)
+
+	//describe Instances
+	instanceB, _ := json.Marshal([]string{instanceId})
+	describeInstancesArgs := &DescribeInstancesArgs{
+		RegionId:    TestRegionID,
+		InstanceIds: string(instanceB),
+	}
+
+	instances, pageInfo, err := client.DescribeInstances(describeInstancesArgs)
+	if err != nil {
+		t.Fatalf("Error %v", err)
+	}
+
+	t.Logf("PageInfo = %#v", pageInfo)
+	for _, instance := range instances {
+		t.Logf("Instance = %#v", instance)
+	}
+
+	time.Sleep(30 * time.Second)
+	t.Logf("After 30s ,delete instance %s", instanceId)
+
+	//stop instance
+	err = client.StopInstance(instanceId, true)
+	if err != nil {
+		t.Fatalf("StopInstance %s error %v", instanceId, err)
+	}
+
+	//wait for stoppend
+	err = client.WaitForInstance(instanceId, Stopped, 60)
+	if err != nil {
+		t.Fatalf("WaitForInstance Stopped error %v", err)
+	}
+	t.Logf("Instance %s is stopped successfully.", instanceId)
+
+	// delete instance
+	err = client.DeleteInstance(instanceId)
+	if err != nil {
+		t.Fatalf("DeleteInstance %s error %v", instanceId, err)
+	}
+	t.Logf("Instance %s is deleted successfully.", instanceId)
+
+}
diff --git a/ecs/nat_gateway.go b/ecs/nat_gateway.go
index d104cf8..a267117 100644
--- a/ecs/nat_gateway.go
+++ b/ecs/nat_gateway.go
@@ -4,6 +4,13 @@ import (
 	"github.com/denverdino/aliyungo/common"
 )
 
+type NatType string
+
+const (
+	NgwNatTypeNormal   = NatType("Normal")
+	NgwNatTypeEnhanced = NatType("Enhanced")
+)
+
 type BandwidthPackageType struct {
 	IpCount   int
 	Bandwidth int
@@ -13,10 +20,12 @@ type BandwidthPackageType struct {
 type CreateNatGatewayArgs struct {
 	RegionId         common.Region
 	VpcId            string
+	VSwitchId        string
 	Spec             string
 	BandwidthPackage []BandwidthPackageType
 	Name             string
 	Description      string
+	NatType          NatType
 	ClientToken      string
 }
 
@@ -75,6 +84,7 @@ type NatGatewaySetType struct {
 	Spec                string
 	Status              string
 	VpcId               string
+	NatType             NatType
 }
 
 type DescribeNatGatewayResponse struct {
diff --git a/ecs/nat_gateway_test.go b/ecs/nat_gateway_test.go
index d4cad05..fb71aad 100644
--- a/ecs/nat_gateway_test.go
+++ b/ecs/nat_gateway_test.go
@@ -38,3 +38,20 @@ func TestClient_DescribeNatGateways(t *testing.T) {
 		}
 	}
 }
+
+func TestClient_CreateNatGateway(t *testing.T) {
+	client := NewVpcTestClientForDebug()
+	args := &CreateNatGatewayArgs{
+		RegionId:  TestRegionID,
+		VpcId:     TestVpcId,
+		VSwitchId: TestVswitchID,
+		NatType:   NgwNatTypeEnhanced,
+	}
+
+	resp, err := client.CreateNatGateway(args)
+	if err != nil {
+		t.Fatalf("Error %++v", err)
+	} else {
+		t.Logf("ngw id :%s", resp.NatGatewayId)
+	}
+}
diff --git a/ecs/networks.go b/ecs/networks.go
index eb6b0ed..3e45937 100644
--- a/ecs/networks.go
+++ b/ecs/networks.go
@@ -86,25 +86,37 @@ func (client *Client) AllocateEipAddress(args *AllocateEipAddressArgs) (EipAddre
 type EipInstanceType string
 
 const (
-	EcsInstance = "EcsInstance"
-	SlbInstance = "SlbInstance"
-	Nat         = "Nat"
-	HaVip       = "HaVip"
+	EcsInstance      = "EcsInstance"
+	SlbInstance      = "SlbInstance"
+	Nat              = "Nat"
+	HaVip            = "HaVip"
+	NetworkInterface = "NetworkInterface"
+)
+
+type AssociateEipAddressMode string
+
+const (
+	NAT          = "NAT"
+	MULTI_BINDED = "MULTI_BINDED"
+	BINDED       = "BINDED"
 )
 
 type AssociateEipAddressArgs struct {
-	AllocationId string
-	InstanceId   string
-	InstanceType EipInstanceType
+	AllocationId     string
+	InstanceId       string
+	InstanceRegionId common.Region
+	InstanceType     EipInstanceType
+	PrivateIpAddress string
+	Mode             AssociateEipAddressMode
 }
 
 type AssociateEipAddressResponse struct {
 	common.Response
 }
 
-// AssociateEipAddress associates EIP address to VM instance
+// AssociateEipAddress associates EIP address to instance
 //
-// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/network&associateeipaddress
+// You can read doc at https://help.aliyun.com/api/vpc/AssociateEipAddress.html
 func (client *Client) AssociateEipAddress(allocationId string, instanceId string) error {
 	args := AssociateEipAddressArgs{
 		AllocationId: allocationId,
@@ -132,10 +144,11 @@ const (
 type AssociatedInstanceType string
 
 const (
-	AssociatedInstanceTypeEcsInstance = AssociatedInstanceType("EcsInstance")
-	AssociatedInstanceTypeSlbInstance = AssociatedInstanceType("SlbInstance")
-	AssociatedInstanceTypeNat         = AssociatedInstanceType("Nat")
-	AssociatedInstanceTypeHaVip       = AssociatedInstanceType("HaVip")
+	AssociatedInstanceTypeEcsInstance      = AssociatedInstanceType("EcsInstance")
+	AssociatedInstanceTypeSlbInstance      = AssociatedInstanceType("SlbInstance")
+	AssociatedInstanceTypeNat              = AssociatedInstanceType("Nat")
+	AssociatedInstanceTypeHaVip            = AssociatedInstanceType("HaVip")
+	AssociatedInstanceTypeNetworkInterface = AssociatedInstanceType("NetworkInterface")
 )
 
 type DescribeEipAddressesArgs struct {
@@ -143,7 +156,7 @@ type DescribeEipAddressesArgs struct {
 	Status                 EipStatus //enum Associating | Unassociating | InUse | Available
 	EipAddress             string
 	AllocationId           string
-	AssociatedInstanceType AssociatedInstanceType //enum EcsInstance | SlbInstance | Nat | HaVip
+	AssociatedInstanceType AssociatedInstanceType //enum EcsInstance | SlbInstance | Nat | HaVip | NetworkInterface
 	AssociatedInstanceId   string                 //绑定的资源的Id。 这是一个过滤器性质的参数,若不指定,则表示不适用该条件对结果进行过滤。 如果要使用该过滤器,必须同时使用AssociatedInstanceType。若InstanceType为EcsInstance,则此处填写ECS实例Id。若InstanceType为SlbInstance,则此处填写VPC类型的私网SLB 的实例ID。若InstanceType为Nat,则此处填写NAT 的实例ID。。若InstanceType为HaVip,则此处填写HaVipId。
 	common.Pagination
 }
@@ -158,6 +171,7 @@ type EipAddressSetType struct {
 	InstanceId         string
 	InstanceType       string
 	Bandwidth          string // Why string
+	PrivateIpAddress   string
 	InternetChargeType common.InternetChargeType
 	OperationLocks     OperationLocksType
 	AllocationTime     util.ISO6801Time
@@ -218,8 +232,10 @@ func (client *Client) ModifyEipAddressAttribute(allocationId string, bandwidth i
 }
 
 type UnallocateEipAddressArgs struct {
-	AllocationId string
-	InstanceId   string
+	AllocationId     string
+	InstanceId       string
+	InstanceType     EipInstanceType
+	PrivateIpAddress string
 }
 
 type UnallocateEipAddressResponse struct {
@@ -238,6 +254,11 @@ func (client *Client) UnassociateEipAddress(allocationId string, instanceId stri
 	return client.Invoke("UnassociateEipAddress", &args, &response)
 }
 
+func (client *Client) NewUnassociateEipAddress(args *UnallocateEipAddressArgs) error {
+	response := UnallocateEipAddressResponse{}
+	return client.Invoke("UnassociateEipAddress", args, &response)
+}
+
 type ReleaseEipAddressArgs struct {
 	AllocationId string
 }
diff --git a/ecs/networks_test.go b/ecs/networks_test.go
index 5f6a36c..53bebca 100644
--- a/ecs/networks_test.go
+++ b/ecs/networks_test.go
@@ -85,6 +85,37 @@ func TestClient_AllocateEipAddress(t *testing.T) {
 
 }
 
+func TestClient_AssociateEipAddress_ENIIP(t *testing.T) {
+	client := NewVpcTestClientForDebug()
+	eipID := "eip-xxx"
+	eniID := "eni-yyy"
+	eniSecIP := "192.168.0.100"
+	region := common.Region("cn-hangzhou")
+
+	err := client.NewAssociateEipAddress(&AssociateEipAddressArgs{
+		AllocationId:     eipID,
+		InstanceId:       eniID,
+		InstanceType:     NetworkInterface,
+		PrivateIpAddress: eniSecIP,
+	})
+	if err != nil {
+		t.Errorf("Failed to associate EIP address: %v", err)
+	}
+	err = client.WaitForEip(region, eipID, EipStatusInUse, DefaultTimeout)
+	if err != nil {
+		t.Errorf("Failed wait associate EIP address: %v", err)
+	}
+	err = client.NewUnassociateEipAddress(&UnallocateEipAddressArgs{
+		AllocationId:     eipID,
+		InstanceId:       eniID,
+		InstanceType:     NetworkInterface,
+		PrivateIpAddress: eniSecIP,
+	})
+	if err != nil {
+		t.Errorf("Failed unassociate EIP address: %v", err)
+	}
+}
+
 func TestClient_DescribeEipAddresses(t *testing.T) {
 	client := NewVpcTestClientForDebug()
 	args := &DescribeEipAddressesArgs{
diff --git a/ecs/route_entry.go b/ecs/route_entry.go
new file mode 100644
index 0000000..ad80f9c
--- /dev/null
+++ b/ecs/route_entry.go
@@ -0,0 +1,61 @@
+package ecs
+
+import "github.com/denverdino/aliyungo/common"
+
+type DescribeRouteEntryListArgs struct {
+	RegionId             string
+	RouteTableId         string
+	DestinationCidrBlock string
+	IpVersion            string
+	MaxResult            int
+	NextHopId            string
+	NextHopType          string
+	NextToken            string
+	RouteEntryId         string
+	RouteEntryName       string
+	RouteEntryType       string
+}
+
+type DescribeRouteEntryListResponse struct {
+	common.Response
+	NextToken   string
+	RouteEntrys struct {
+		RouteEntry []RouteEntry
+	}
+}
+
+type RouteEntry struct {
+	DestinationCidrBlock string
+	IpVersion            string
+	RouteEntryId         string
+	RouteEntryName       string
+	RouteTableId         string
+	Status               string
+	Type                 string
+	NextHops             struct {
+		NextHop []NextHop
+	}
+}
+
+type NextHop struct {
+	Enabled            int
+	Weight             int
+	NextHopId          string
+	NextHopRegionId    string
+	NextHopType        string
+	NextHopRelatedInfo NextHopRelatedInfo
+}
+
+type NextHopRelatedInfo struct {
+	RegionId     string
+	InstanceId   string
+	InstanceType string
+}
+
+// DescribeRouteEntryList describes route entries
+//
+func (client *Client) DescribeRouteEntryList(args *DescribeRouteEntryListArgs) (*DescribeRouteEntryListResponse, error) {
+	response := &DescribeRouteEntryListResponse{}
+	err := client.Invoke("DescribeRouteEntryList", args, &response)
+	return response, err
+}
diff --git a/ecs/route_entry_test.go b/ecs/route_entry_test.go
new file mode 100644
index 0000000..6eb4f50
--- /dev/null
+++ b/ecs/route_entry_test.go
@@ -0,0 +1,27 @@
+package ecs
+
+import (
+	"testing"
+)
+
+func TestClient_DescribeRouteEntry(t *testing.T) {
+	client := NewVpcTestClientForDebug()
+
+	nextHopId := "i-xxxx"
+	destinationCidrBlock := "172.xxx/x"
+	args := &DescribeRouteEntryListArgs{
+		RegionId:             "cn-hangzhou",
+		RouteTableId:         "vtb-xxxxx",
+		DestinationCidrBlock: destinationCidrBlock,
+		NextHopId:            nextHopId,
+		RouteEntryType:       "Custom",
+	}
+
+	response, err := client.DescribeRouteEntryList(args)
+
+	if err != nil {
+		t.Fatalf("Error %++v", err)
+	} else {
+		t.Logf("Result %++v", response)
+	}
+}
diff --git a/ecs/route_tables.go b/ecs/route_tables.go
index 15d7c7b..dc800ec 100644
--- a/ecs/route_tables.go
+++ b/ecs/route_tables.go
@@ -100,9 +100,13 @@ func (client *Client) DescribeRouteTablesWithRaw(args *DescribeRouteTablesArgs)
 type NextHopType string
 
 const (
-	NextHopIntance               = NextHopType("Instance") //Default
-	NextHopTunnel                = NextHopType("Tunnel")
-	NextHopTunnelRouterInterface = NextHopType("RouterInterface")
+	NextHopInstance         = NextHopType("Instance") //Default
+	NextHopHaVip            = NextHopType("HaVip")
+	NextHopRouterInterface  = NextHopType("RouterInterface")
+	NextHopNetworkInterface = NextHopType("NetworkInterface")
+	NextHopVpnGateway       = NextHopType("VpnGateway")
+	NextHopIPv6Gateway      = NextHopType("IPv6Gateway")
+	NextHopTunnel           = NextHopType("Tunnel")
 )
 
 type CreateRouteEntryArgs struct {
diff --git a/ecs/route_tables_test.go b/ecs/route_tables_test.go
index e0fe172..ec6b8df 100644
--- a/ecs/route_tables_test.go
+++ b/ecs/route_tables_test.go
@@ -11,7 +11,7 @@ func testRouteTable(t *testing.T, client *Client, regionId common.Region, vpcId
 	createArgs := CreateRouteEntryArgs{
 		RouteTableId:         routeTableId,
 		DestinationCidrBlock: cidrBlock,
-		NextHopType:          NextHopIntance,
+		NextHopType:          NextHopInstance,
 		NextHopId:            instanceId,
 		ClientToken:          client.GenerateClientToken(),
 	}
diff --git a/ecs/router_interface.go b/ecs/router_interface.go
index 188bac8..b39c5dd 100644
--- a/ecs/router_interface.go
+++ b/ecs/router_interface.go
@@ -238,7 +238,7 @@ func (client *Client) WaitForRouterInterfaceAsyn(regionId common.Region, interfa
 	for {
 		interfaces, err := client.DescribeRouterInterfaces(&DescribeRouterInterfacesArgs{
 			RegionId: regionId,
-			Filter:   []Filter{Filter{Key: "RouterInterfaceId", Value: []string{interfaceId}}},
+			Filter:   []Filter{{Key: "RouterInterfaceId", Value: []string{interfaceId}}},
 		})
 		if err != nil {
 			return err
diff --git a/ecs/security_groups.go b/ecs/security_groups.go
index dbe8543..844f30f 100644
--- a/ecs/security_groups.go
+++ b/ecs/security_groups.go
@@ -7,6 +7,7 @@ import (
 
 type NicType string
 type Direction string
+type SecurityGroupType string
 
 const (
 	NicTypeInternet = NicType("internet")
@@ -15,6 +16,9 @@ const (
 	DirectionIngress = Direction("ingress")
 	DirectionEgress  = Direction("egress")
 	DirectionAll     = Direction("all")
+
+	SecurityGroupTypeNormal     = SecurityGroupType("normal")
+	SecurityGroupTypeEnterprise = SecurityGroupType("enterprise")
 )
 
 type IpProtocol string
@@ -92,8 +96,9 @@ func (client *Client) DescribeSecurityGroupAttribute(args *DescribeSecurityGroup
 }
 
 type DescribeSecurityGroupsArgs struct {
-	RegionId common.Region
-	VpcId    string
+	RegionId         common.Region
+	VpcId            string
+	SecurityGroupIds []string
 	common.Pagination
 }
 
@@ -104,6 +109,8 @@ type SecurityGroupItemType struct {
 	SecurityGroupName string
 	Description       string
 	VpcId             string
+	SecurityGroupType SecurityGroupType // normal|enterprise
+	ServiceManaged    bool
 	CreationTime      util.ISO6801Time
 }
 
diff --git a/ecs/security_groups_test.go b/ecs/security_groups_test.go
index 7ca8ea5..e608fe7 100644
--- a/ecs/security_groups_test.go
+++ b/ecs/security_groups_test.go
@@ -1,6 +1,8 @@
 package ecs
 
 import (
+	"os"
+	"strings"
 	"testing"
 
 	"github.com/denverdino/aliyungo/common"
@@ -42,6 +44,23 @@ func TestSecurityGroups(t *testing.T) {
 	}
 }
 
+func TestSecurityGroups_BatchQuery(t *testing.T) {
+
+	client := NewTestClient()
+	arg := DescribeSecurityGroupsArgs{
+		RegionId:         common.Region(os.Getenv("RegionId")),
+		SecurityGroupIds: strings.Split(os.Getenv("SecurityGroupIDs"), ","),
+	}
+
+	sgs, _, err := client.DescribeSecurityGroups(&arg)
+	if err != nil {
+		t.Errorf("Failed to DescribeSecurityGroups, error:%++v", err)
+	}
+	for _, sg := range sgs {
+		t.Logf("SecurityGroup: %++v", sg)
+	}
+}
+
 func TestECSSecurityGroupCreationAndDeletion(t *testing.T) {
 	client := NewTestClient()
 	instance, err := client.DescribeInstanceAttribute(TestInstanceId)
diff --git a/ecs/snapshots.go b/ecs/snapshots.go
index 0b7cbe8..e9a81f1 100644
--- a/ecs/snapshots.go
+++ b/ecs/snapshots.go
@@ -126,7 +126,7 @@ func (client *Client) WaitForSnapShotReady(regionId common.Region, snapshotId st
 		if err != nil {
 			return err
 		}
-		if snapshots == nil || len(snapshots) == 0 {
+		if len(snapshots) == 0 {
 			return common.GetClientErrorFromString("Not found")
 		}
 		if snapshots[0].Progress == "100%" {
diff --git a/ecs/snat_entry.go b/ecs/snat_entry.go
index c2e20df..9d03415 100644
--- a/ecs/snat_entry.go
+++ b/ecs/snat_entry.go
@@ -1,12 +1,24 @@
 package ecs
 
-import "github.com/denverdino/aliyungo/common"
+import (
+	"time"
+
+	"github.com/denverdino/aliyungo/common"
+)
+
+type SnatEntryStatus string
+
+const (
+	SnatEntryStatusPending   = SnatEntryStatus("Pending")
+	SnatEntryStatusAvailable = SnatEntryStatus("Available")
+)
 
 type CreateSnatEntryArgs struct {
 	RegionId        common.Region
 	SnatTableId     string
 	SourceVSwitchId string
 	SnatIp          string
+	SourceCIDR      string
 }
 
 type CreateSnatEntryResponse struct {
@@ -21,12 +33,17 @@ type SnatEntrySetType struct {
 	SnatTableId     string
 	SourceCIDR      string
 	SourceVSwitchId string
-	Status          string
+	Status          SnatEntryStatus
 }
 
 type DescribeSnatTableEntriesArgs struct {
-	RegionId    common.Region
-	SnatTableId string
+	RegionId        common.Region
+	SnatTableId     string
+	SnatEntryId     string
+	SnatEntryName   string
+	SnatIp          string
+	SourceCIDR      string
+	SourceVSwitchId string
 	common.Pagination
 }
 
@@ -39,10 +56,11 @@ type DescribeSnatTableEntriesResponse struct {
 }
 
 type ModifySnatEntryArgs struct {
-	RegionId    common.Region
-	SnatTableId string
-	SnatEntryId string
-	SnatIp      string
+	RegionId      common.Region
+	SnatTableId   string
+	SnatEntryId   string
+	SnatIp        string
+	SnatEntryName string
 }
 
 type ModifySnatEntryResponse struct {
@@ -101,3 +119,37 @@ func (client *Client) DeleteSnatEntry(args *DeleteSnatEntryArgs) error {
 	err := client.Invoke("DeleteSnatEntry", args, &response)
 	return err
 }
+
+// WaitForSnatEntryAvailable waits for SnatEntry to available status
+func (client *Client) WaitForSnatEntryAvailable(regionId common.Region, snatTableId, snatEntryId string, timeout int) error {
+	if timeout <= 0 {
+		timeout = DefaultTimeout
+	}
+
+	args := &DescribeSnatTableEntriesArgs{
+		RegionId:    regionId,
+		SnatTableId: snatTableId,
+		SnatEntryId: snatEntryId,
+	}
+
+	for {
+		snatEntries, _, err := client.DescribeSnatTableEntries(args)
+		if err != nil {
+			return err
+		}
+
+		if len(snatEntries) == 0 {
+			return common.GetClientErrorFromString("Not found")
+		}
+		if snatEntries[0].Status == SnatEntryStatusAvailable {
+			break
+		}
+
+		timeout = timeout - DefaultWaitForInterval
+		if timeout <= 0 {
+			return common.GetClientErrorFromString("Timeout")
+		}
+		time.Sleep(DefaultWaitForInterval * time.Second)
+	}
+	return nil
+}
diff --git a/ecs/snat_entry_test.go b/ecs/snat_entry_test.go
index 824218a..d42e054 100644
--- a/ecs/snat_entry_test.go
+++ b/ecs/snat_entry_test.go
@@ -14,3 +14,18 @@ func TestDescribeSnatTableEntry(t *testing.T) {
 		t.Fatalf("Failed to DescribeBandwidthPackages: %v", err)
 	}
 }
+
+func TestCreateSnatEntryWithSourceCIDR(t *testing.T) {
+	client := NewTestClient()
+	args := CreateSnatEntryArgs{
+		RegionId:    "cn-beijing",
+		SnatTableId: "stb-xxx",
+		SnatIp:      "47.XX.XX.98",
+		SourceCIDR:  "192.168.1.1/32",
+	}
+
+	_, err := client.CreateSnatEntry(&args)
+	if err != nil {
+		t.Errorf("failed to CreateSnatEntry with SourceCIDR: %v", err)
+	}
+}
diff --git a/ecs/vpcs.go b/ecs/vpcs.go
index d66fe10..f3b45ae 100644
--- a/ecs/vpcs.go
+++ b/ecs/vpcs.go
@@ -76,11 +76,17 @@ type VpcSetType struct {
 	VSwitchIds struct {
 		VSwitchId []string
 	}
-	CidrBlock    string
-	VRouterId    string
-	Description  string
-	IsDefault    bool
-	CreationTime util.ISO6801Time
+	CidrBlock           string
+	VRouterId           string
+	Description         string
+	IsDefault           bool
+	CreationTime        util.ISO6801Time
+	SecondaryCidrBlocks struct {
+		SecondaryCidrBlock []string
+	}
+	RouterTableIds struct {
+		RouterTableIds []string
+	}
 }
 
 type DescribeVpcsResponse struct {
diff --git a/ecs/vpcs_test.go b/ecs/vpcs_test.go
index 8baadcf..798b19d 100644
--- a/ecs/vpcs_test.go
+++ b/ecs/vpcs_test.go
@@ -164,7 +164,11 @@ func testCreateInstanceVpc(t *testing.T, client *Client, regionId common.Region,
 	t.Logf("Instance %s is created successfully.", instanceId)
 	instance, err := client.DescribeInstanceAttribute(instanceId)
 	t.Logf("Instance: %++v  %v", instance, err)
+
 	err = client.WaitForInstance(instanceId, Stopped, 0)
+	if err != nil {
+		t.Errorf("Failed to wait instance to stopped %s: %v", instanceId, err)
+	}
 
 	err = client.StartInstance(instanceId)
 	if err != nil {
diff --git a/ess/configuration.go b/ess/configuration.go
index 092c320..52c64cc 100644
--- a/ess/configuration.go
+++ b/ess/configuration.go
@@ -2,7 +2,6 @@ package ess
 
 import (
 	"encoding/base64"
-
 	"github.com/denverdino/aliyungo/common"
 	"github.com/denverdino/aliyungo/ecs"
 )
@@ -90,6 +89,7 @@ type ScalingConfigurationItemType struct {
 	CreationTime             string
 	InternetMaxBandwidthIn   int
 	InternetMaxBandwidthOut  int
+	SystemDiskSize           int
 	SystemDiskCategory       string
 	DataDisks                struct {
 		DataDisk []DataDiskItemType
@@ -167,3 +167,83 @@ func (client *Client) DeactivateScalingConfiguration(args *DeactivateScalingConf
 	}
 	return &response, nil
 }
+
+type ModifyScalingConfigurationRequest struct {
+	ScalingConfigurationId   string
+	IoOptimized              string
+	DataDisk                 []DataDisk
+	SpotStrategy             string
+	SpotPriceLimit           []SpotPriceLimit
+	ScalingConfigurationName string
+	InstanceName             string
+	HostName                 string
+	ImageId                  string
+	ImageName                string
+	InstanceTypes            []string
+	Cpu                      int
+	Memory                   int
+	InternetChargeType       string
+	InternetMaxBandwidthOut  int
+	SystemDisk               SystemDisk
+	LoadBalancerWeight       string
+	UserData                 string
+	KeyPairName              string
+	RamRoleName              string
+	PasswordInherit          string
+	Tags                     string
+	DeploymentSetId          string
+	SecurityGroupId          string
+	Override                 bool
+	ResourceGroupId          string
+	SecurityGroupIds         []string
+	HpcClusterId             string
+
+	InstanceDescription string
+	Ipv6AddressCount    int
+
+	CreditSpecification string
+	ImageFamily         string
+	DedicatedHostId     string
+	Affinity            string
+	Tenancy             string
+}
+
+type DataDisk struct {
+	Size                 int
+	SnapshotId           string
+	Category             string
+	Device               string
+	DeleteWithInstance   bool
+	Encrypted            bool
+	KMSKeyId             string
+	DiskName             string
+	Description          string
+	AutoSnapshotPolicyId string
+}
+
+type SpotPriceLimit struct {
+	InstanceType string
+	PriceLimit   float32
+}
+
+type SystemDisk struct {
+	Category             string
+	Size                 int
+	DiskName             string
+	Description          string
+	AutoSnapshotPolicyId string
+}
+
+type ModifyScalingConfigurationResponse struct {
+	common.Response
+}
+
+func (client *Client) ModifyScalingConfiguration(args *ModifyScalingConfigurationRequest) (resp *ModifyScalingConfigurationResponse, err error) {
+	response := ModifyScalingConfigurationResponse{}
+	err = client.InvokeByFlattenMethod("ModifyScalingConfiguration", args, &response)
+
+	if err != nil {
+		return nil, err
+	}
+	return &response, nil
+}
diff --git a/ess/group.go b/ess/group.go
index 65f3862..cf35b63 100644
--- a/ess/group.go
+++ b/ess/group.go
@@ -17,6 +17,14 @@ const (
 	Removing  = LifecycleState("Removing")
 )
 
+type MultiAZPolicy string
+
+const (
+	MultiAZPolicyPriority      = MultiAZPolicy("PRIORITY")
+	MultiAZPolicyCostOptimized = MultiAZPolicy("COST_OPTIMIZED")
+	MultiAZPolicyBalance       = MultiAZPolicy("BALANCE")
+)
+
 type CreateScalingGroupArgs struct {
 	RegionId         common.Region
 	ScalingGroupName string
@@ -27,6 +35,7 @@ type CreateScalingGroupArgs struct {
 	// NOTE: Set MinSize, MaxSize and DefaultCooldown type to int pointer to distinguish zero value from unset value.
 	MinSize         *int
 	MaxSize         *int
+	MultiAZPolicy   MultiAZPolicy
 	DefaultCooldown *int
 	RemovalPolicy   common.FlattenArray
 	DBInstanceIds   string
@@ -338,3 +347,45 @@ func (client *Client) WaitForScalingGroup(regionId common.Region, groupId string
 	}
 	return nil
 }
+
+type DescribeScalingActivitiesRequest struct {
+	ScalingActivityId []string
+	ScalingGroupId    string
+	StatusCode        string
+	RegionId          common.Region
+	common.Pagination
+}
+
+type DescribeScalingActivitiesResponse struct {
+	common.PaginationResult
+	common.Response
+	ScalingActivities struct {
+		ScalingActivity []ScalingActivity
+	}
+}
+
+type ScalingActivity struct {
+	AttachedCapacity      int
+	AutoCreatedCapacity   int
+	Cause                 string
+	Description           string
+	EndTime               string
+	Progress              int
+	ScalingActivityId     string
+	ScalingGroupId        string
+	ScalingInstanceNumber int
+	StartTime             string
+	StatusCode            string
+	StatusMessage         string
+	TotalCapacity         int
+}
+
+func (client *Client) DescribeScalingActivities(args *DescribeScalingActivitiesRequest) (resp *DescribeScalingActivitiesResponse, err error) {
+	response := DescribeScalingActivitiesResponse{}
+	err = client.InvokeByFlattenMethod("DescribeScalingActivities", args, &response)
+
+	if err != nil {
+		return nil, err
+	}
+	return &response, nil
+}
diff --git a/ess/group_test.go b/ess/group_test.go
index 5161ead..46d0c47 100644
--- a/ess/group_test.go
+++ b/ess/group_test.go
@@ -1,6 +1,7 @@
 package ess
 
 import (
+	"fmt"
 	"testing"
 
 	"github.com/denverdino/aliyungo/common"
@@ -75,3 +76,20 @@ func TestEssScalingGroupCreationAndDeletion(t *testing.T) {
 	}
 	t.Logf("Instance %s is deleted successfully.", instanceId)
 }
+
+func TestEssScalingActivity(t *testing.T) {
+
+	client := NewTestClient(common.Region(RegionId))
+	id := "asg-uf68jfxw7gqlao0wlc94"
+	result, err := client.DescribeScalingActivities(
+		&DescribeScalingActivitiesRequest{
+			ScalingGroupId: id,
+			RegionId:       common.Shanghai,
+		},
+	)
+	if err != nil {
+		t.Errorf("get activity %s: %v", id, err)
+	}
+	fmt.Printf("%+v\n", result)
+	t.Logf("get activity succeed by id %s.", id)
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..ca4da2e
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,15 @@
+module github.com/denverdino/aliyungo
+
+go 1.15
+
+require (
+	github.com/golang/protobuf v1.5.2 // indirect
+	github.com/magiconair/properties v1.8.6 // indirect
+	github.com/opentracing/opentracing-go v1.2.0 // indirect
+	github.com/pkg/errors v0.9.1 // indirect
+	github.com/stretchr/testify v1.7.1 // indirect
+	github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect
+	github.com/uber/jaeger-lib v2.4.1+incompatible // indirect
+	go.uber.org/atomic v1.9.0 // indirect
+	golang.org/x/text v0.3.7 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..980a857
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,38 @@
+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/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
+github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
+github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
+github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
+github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+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/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o=
+github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
+github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg=
+github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
+go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
+go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/location/regions_test.go b/location/regions_test.go
index fe5730b..598d179 100644
--- a/location/regions_test.go
+++ b/location/regions_test.go
@@ -6,7 +6,7 @@ func TestDescribeRegions(t *testing.T) {
 	client := NewTestClientForDebug()
 
 	args := &DescribeRegionsArgs{
-	//RegionId: common.Beijing,
+		//RegionId: common.Beijing,
 	}
 
 	regions, err := client.DescribeRegions(args)
diff --git a/metadata/client.go b/metadata/client.go
index 964f05f..4fac134 100644
--- a/metadata/client.go
+++ b/metadata/client.go
@@ -14,8 +14,9 @@ import (
 	"encoding/json"
 	"reflect"
 
-	"github.com/denverdino/aliyungo/util"
 	"os"
+
+	"github.com/denverdino/aliyungo/util"
 )
 
 const (
@@ -44,7 +45,7 @@ const (
 	VSWITCH_CIDR_BLOCK = "vswitch-cidr-block"
 	VSWITCH_ID         = "vswitch-id"
 	ZONE               = "zone-id"
-	RAM_SECURITY       = "Ram/security-credentials"
+	RAM_SECURITY       = "ram/security-credentials"
 )
 
 type IMetaDataRequest interface {
@@ -351,7 +352,7 @@ func (vpc *MetaDataRequest) Do(api interface{}) (err error) {
 func (vpc *MetaDataRequest) Decode(data string, api interface{}) error {
 	if data == "" {
 		url, _ := vpc.Url()
-		return errors.New(fmt.Sprintf("metadata: alivpc decode data must not be nil. url=[%s]\n", url))
+		return fmt.Errorf("metadata: alivpc decode data must not be nil. url=[%s]\n", url)
 	}
 	switch api.(type) {
 	case *ResultList:
@@ -360,7 +361,7 @@ func (vpc *MetaDataRequest) Decode(data string, api interface{}) error {
 	case *RoleAuth:
 		return json.Unmarshal([]byte(data), api)
 	default:
-		return errors.New(fmt.Sprintf("metadata: unknow type to decode, type=%s\n", reflect.TypeOf(api)))
+		return fmt.Errorf("metadata: unknow type to decode, type=%s\n", reflect.TypeOf(api))
 	}
 }
 
diff --git a/mns/client.go b/mns/client.go
index 56482da..5798d96 100644
--- a/mns/client.go
+++ b/mns/client.go
@@ -22,6 +22,14 @@ func (client *Client) SetDebug(debug bool) {
 	client.debug = debug
 }
 
+// SetTransport sets transport to the http client
+func (client *Client) SetTransport(transport http.RoundTripper) {
+	if client.httpClient == nil {
+		client.httpClient = &http.Client{}
+	}
+	client.httpClient.Transport = transport
+}
+
 func NewClient(accessKeyId, accessKeySecret, endpoint string) (client *Client) {
 	client = &Client{
 		AccessKeyId:     accessKeyId,
diff --git a/mns/signature.go b/mns/signature.go
index 084b669..64a2209 100644
--- a/mns/signature.go
+++ b/mns/signature.go
@@ -44,7 +44,7 @@ func canonicalizeResource(req *request) string {
 	canonicalizedResource := req.path
 	var paramNames []string
 	if req.params != nil && len(req.params) > 0 {
-		for k, _ := range req.params {
+		for k := range req.params {
 			paramNames = append(paramNames, k)
 		}
 		sort.Strings(paramNames)
@@ -62,7 +62,7 @@ func canonicalizeResource(req *request) string {
 func canonicalizeHeader(headers map[string]string) string {
 	var canonicalizedHeaders []string
 
-	for k, _ := range headers {
+	for k := range headers {
 		if lower := strings.ToLower(k); strings.HasPrefix(lower, HeaderMNSPrefix) {
 			canonicalizedHeaders = append(canonicalizedHeaders, lower)
 		}
diff --git a/oss/client.go b/oss/client.go
index 437fca0..eda3bce 100644
--- a/oss/client.go
+++ b/oss/client.go
@@ -37,6 +37,7 @@ type Client struct {
 	Internal        bool
 	Secure          bool
 	ConnectTimeout  time.Duration
+	Transport       http.RoundTripper
 
 	endpoint string
 	debug    bool
@@ -57,12 +58,14 @@ type Owner struct {
 // Options struct
 //
 type Options struct {
-	ServerSideEncryption bool
-	Meta                 map[string][]string
-	ContentEncoding      string
-	CacheControl         string
-	ContentMD5           string
-	ContentDisposition   string
+	ServerSideEncryption      bool
+	ServerSideEncryptionKeyID string
+
+	Meta               map[string][]string
+	ContentEncoding    string
+	CacheControl       string
+	ContentMD5         string
+	ContentDisposition string
 	//Range              string
 	//Expires int
 }
@@ -72,6 +75,9 @@ type CopyOptions struct {
 	CopySourceOptions string
 	MetadataDirective string
 	//ContentType       string
+
+	ServerSideEncryption      bool
+	ServerSideEncryptionKeyID string
 }
 
 // CopyObjectResult is the output from a Copy request
@@ -491,7 +497,10 @@ func (b *Bucket) PutFile(path string, file *os.File, perm ACL, options Options)
 
 // addHeaders adds o's specified fields to headers
 func (o Options) addHeaders(headers http.Header) {
-	if o.ServerSideEncryption {
+	if len(o.ServerSideEncryptionKeyID) != 0 {
+		headers.Set("x-oss-server-side-encryption", "KMS")
+		headers.Set("x-oss-server-side-encryption-key-id", o.ServerSideEncryptionKeyID)
+	} else if o.ServerSideEncryption {
 		headers.Set("x-oss-server-side-encryption", "AES256")
 	}
 	if len(o.ContentEncoding) != 0 {
@@ -516,6 +525,13 @@ func (o Options) addHeaders(headers http.Header) {
 
 // addHeaders adds o's specified fields to headers
 func (o CopyOptions) addHeaders(headers http.Header) {
+	if len(o.ServerSideEncryptionKeyID) != 0 {
+		headers.Set("x-oss-server-side-encryption", "KMS")
+		headers.Set("x-oss-server-side-encryption-key-id", o.ServerSideEncryptionKeyID)
+	} else if o.ServerSideEncryption {
+		headers.Set("x-oss-server-side-encryption", "AES256")
+	}
+
 	if len(o.MetadataDirective) != 0 {
 		headers.Set("x-oss-metadata-directive", o.MetadataDirective)
 	}
@@ -836,6 +852,17 @@ func (b *Bucket) SignedURLWithArgs(path string, expires time.Time, params url.Va
 	return b.SignedURLWithMethod("GET", path, expires, params, headers)
 }
 
+func (b *Bucket) SignedURLWithMethodForAssumeRole(method, path string, expires time.Time, params url.Values, headers http.Header) string {
+	var uv = url.Values{}
+	if params != nil {
+		uv = params
+	}
+	if len(b.Client.SecurityToken) != 0 {
+		uv.Set("security-token", b.Client.SecurityToken)
+	}
+	return b.SignedURLWithMethod(method, path, expires, params, headers)
+}
+
 // SignedURLWithMethod returns a signed URL that allows anyone holding the URL
 // to either retrieve the object at path or make a HEAD request against it. The signature is valid until expires.
 func (b *Bucket) SignedURLWithMethod(method, path string, expires time.Time, params url.Values, headers http.Header) string {
@@ -1024,7 +1051,8 @@ func partiallyEscapedPath(path string) string {
 func (client *Client) prepare(req *request) error {
 	// Copy so they can be mutated without affecting on retries.
 	headers := copyHeader(req.headers)
-	if len(client.SecurityToken) != 0 {
+	// security-token should be in either Params or Header, cannot be in both
+	if len(req.params.Get("security-token")) == 0 && len(client.SecurityToken) != 0 {
 		headers.Set("x-oss-security-token", client.SecurityToken)
 	}
 
@@ -1162,6 +1190,9 @@ func (client *Client) run(req *request, resp interface{}) (*http.Response, error
 		},
 		Timeout: req.timeout,
 	}
+	if client.Transport != nil {
+		c.Transport = client.Transport
+	}
 
 	return client.doHttpRequest(c, hreq, resp)
 }
diff --git a/oss/client_test.go b/oss/client_test.go
index e6bbab8..d442f0e 100644
--- a/oss/client_test.go
+++ b/oss/client_test.go
@@ -6,9 +6,10 @@ import (
 	"io/ioutil"
 	"math/rand"
 	"net/http"
-	"os"
+	"net/url"
 	"strconv"
 	"sync"
+
 	//"net/http"
 	"testing"
 	"time"
@@ -23,15 +24,8 @@ var (
 )
 
 func init() {
-	AccessKeyId := os.Getenv("AccessKeyId")
-	AccessKeySecret := os.Getenv("AccessKeySecret")
-	if len(AccessKeyId) != 0 && len(AccessKeySecret) != 0 {
-		client = oss.NewOSSClient(TestRegion, false, AccessKeyId, AccessKeySecret, false)
-	} else {
-		client = oss.NewOSSClient(TestRegion, false, TestAccessKeyId, TestAccessKeySecret, false)
-	}
-
-	assumeRoleClient = oss.NewOSSClientForAssumeRole(TestRegion, false, TestAccessKeyId, TestAccessKeySecret, TestSecurityToken, false)
+	client = oss.NewOSSClient(TestRegion, false, TestAccessKeyID, TestAccessKeySecret, false)
+	assumeRoleClient = oss.NewOSSClientForAssumeRole(TestRegion, false, TestAccessKeyID, TestAccessKeySecret, TestSecurityToken, false)
 	assumeRoleClient.SetDebug(true)
 }
 
@@ -98,7 +92,7 @@ func TestGetReader(t *testing.T) {
 	}
 }
 
-func aTestGetNotFound(t *testing.T) {
+func TestGetNotFound(t *testing.T) {
 
 	b := client.Bucket("non-existent-bucket")
 	_, err := b.Get("non-existent")
@@ -124,12 +118,39 @@ func TestPutCopy(t *testing.T) {
 	}
 }
 
+func TestPutObjectWithSSE(t *testing.T) {
+	const DISPOSITION = "attachment; filename=\"0x1a2b3c.jpg\""
+
+	b := client.Bucket(TestBucket)
+	err := b.Put("name-sse", []byte("content"), "content-type", oss.Private, oss.Options{
+		ContentDisposition:        DISPOSITION,
+		ServerSideEncryptionKeyID: TestServerSideEncryptionKeyID,
+	})
+	if err != nil {
+		t.Errorf("Failed for Put: %v", err)
+	}
+}
+
+func TestPutCopyWithSSE(t *testing.T) {
+	b := client.Bucket(TestBucket)
+	t.Log("Source: ", b.Path("name-sse"))
+	res, err := b.PutCopy("newname-sse", oss.Private, oss.CopyOptions{
+		ServerSideEncryptionKeyID: TestServerSideEncryptionKeyID,
+	},
+		b.Path("name"))
+	if err == nil {
+		t.Logf("Copy result: %v", res)
+	} else {
+		t.Errorf("Failed for PutCopy: %v", err)
+	}
+}
+
 func TestList(t *testing.T) {
 
 	b := client.Bucket(TestBucket)
 
 	data, err := b.List("n", "", "", 0)
-	if err != nil || len(data.Contents) != 2 {
+	if err != nil || len(data.Contents) != 4 {
 		t.Errorf("Failed for List: %v", err)
 	} else {
 		t.Logf("Contents = %++v", data)
@@ -241,6 +262,30 @@ func TestSignedURL(t *testing.T) {
 	resp.Body.Close()
 }
 
+func TestSignedURLWithArgsWithTrafficLimits(t *testing.T) {
+	b := client.Bucket(TestBucket)
+	expires := time.Now().Add(20 * time.Minute)
+	url := b.SignedURLWithArgs("largefile", expires, url.Values{
+		"x-oss-traffic-limit": []string{strconv.FormatInt(5*8<<20, 10)}},
+		nil)
+	resp, err := http.Get(url)
+	t.Logf("Large file response headers: %++v", resp.Header)
+
+	if err != nil {
+		t.Fatalf("Failed for GetResponseWithHeaders: %v", err)
+	}
+	data, err := ioutil.ReadAll(resp.Body)
+
+	if err != nil {
+		t.Errorf("Failed for Read file: %v", err)
+	}
+
+	if len(data) != int(_fileSize) {
+		t.Errorf("Incorrect length for Read with offset: %v", len(data))
+	}
+	resp.Body.Close()
+}
+
 func TestCopyLargeFile(t *testing.T) {
 	b := client.Bucket(TestBucket)
 	err := b.CopyLargeFile("largefile", "largefile2", "application/octet-stream", oss.Private, oss.Options{})
@@ -272,7 +317,7 @@ func TestCopyLargeFile(t *testing.T) {
 		t.Fatalf("Failed for Get file: %v", err)
 	}
 
-	if bytes.Compare(bytes1, bytes2) != 0 {
+	if !bytes.Equal(bytes1, bytes2) {
 		t.Fatal("The result should be equal")
 	}
 }
@@ -308,7 +353,7 @@ func TestCopyLargeFileInParallel(t *testing.T) {
 		t.Fatalf("Failed for Get file: %v", err)
 	}
 
-	if bytes.Compare(bytes1, bytes2) != 0 {
+	if !bytes.Equal(bytes1, bytes2) {
 		t.Fatal("The result should be equal")
 	}
 }
@@ -371,7 +416,11 @@ func TestDelObject(t *testing.T) {
 func TestDelMultiObjects(t *testing.T) {
 
 	b := client.Bucket(TestBucket)
-	objects := []oss.Object{oss.Object{Key: "newname"}}
+	objects := []oss.Object{
+		{Key: "newname"},
+		{Key: "name-sse"},
+		{Key: "newname-sse"},
+	}
 	err := b.DelMulti(oss.Delete{
 		Quiet:   false,
 		Objects: objects,
diff --git a/oss/config_test.go b/oss/config_test.go
index 4c3b665..30f1076 100644
--- a/oss/config_test.go
+++ b/oss/config_test.go
@@ -16,8 +16,10 @@ import (
 //
 
 var (
-	TestAccessKeyId     = os.Getenv("AccessKeyId")
+	TestAccessKeyID     = os.Getenv("AccessKeyId")
 	TestAccessKeySecret = os.Getenv("AccessKeySecret")
 	TestSecurityToken   = os.Getenv("SecurityToken")
 	TestRegion          = oss.Region(os.Getenv("RegionId"))
+
+	TestServerSideEncryptionKeyID = os.Getenv("ServerSideEncryptionKeyId")
 )
diff --git a/oss/regions.go b/oss/regions.go
index a82d7cf..89bac83 100644
--- a/oss/regions.go
+++ b/oss/regions.go
@@ -27,6 +27,7 @@ const (
 	MEEast1 = Region("oss-me-east-1")
 
 	EUCentral1 = Region("oss-eu-central-1")
+	EUWest1    = Region("oss-eu-west-1")
 
 	DefaultRegion = Hangzhou
 )
diff --git a/oss/signature.go b/oss/signature.go
index 1267717..ced4b1e 100644
--- a/oss/signature.go
+++ b/oss/signature.go
@@ -13,26 +13,45 @@ const HeaderOSSPrefix = "x-oss-"
 
 var ossParamsToSign = map[string]bool{
 	"acl":                          true,
+	"append":                       true,
+	"bucketInfo":                   true,
+	"cname":                        true,
+	"comp":                         true,
+	"cors":                         true,
 	"delete":                       true,
+	"endTime":                      true,
+	"img":                          true,
+	"lifecycle":                    true,
+	"live":                         true,
 	"location":                     true,
 	"logging":                      true,
-	"notification":                 true,
+	"objectMeta":                   true,
 	"partNumber":                   true,
-	"policy":                       true,
-	"requestPayment":               true,
-	"torrent":                      true,
-	"uploadId":                     true,
-	"uploads":                      true,
-	"versionId":                    true,
-	"versioning":                   true,
-	"versions":                     true,
-	"response-content-type":        true,
-	"response-content-language":    true,
-	"response-expires":             true,
+	"position":                     true,
+	"qos":                          true,
+	"referer":                      true,
+	"replication":                  true,
+	"replicationLocation":          true,
+	"replicationProgress":          true,
 	"response-cache-control":       true,
 	"response-content-disposition": true,
 	"response-content-encoding":    true,
-	"bucketInfo":                   true,
+	"response-content-language":    true,
+	"response-content-type":        true,
+	"response-expires":             true,
+	"security-token":               true,
+	"startTime":                    true,
+	"status":                       true,
+	"style":                        true,
+	"styleName":                    true,
+	"symlink":                      true,
+	"tagging":                      true,
+	"uploadId":                     true,
+	"uploads":                      true,
+	"vod":                          true,
+	"website":                      true,
+	"x-oss-process":                true,
+	"x-oss-traffic-limit":          true,
 }
 
 func (client *Client) signRequest(request *request) {
@@ -62,7 +81,7 @@ func (client *Client) signRequest(request *request) {
 	}
 
 	if len(params) > 0 {
-		resource = resource + "?" + util.Encode(params)
+		resource = resource + "?" + util.EncodeWithoutEscape(params)
 	}
 
 	canonicalizedResource := resource
@@ -74,7 +93,7 @@ func (client *Client) signRequest(request *request) {
 	//log.Println("stringToSign: ", stringToSign)
 	signature := util.CreateSignature(stringToSign, client.AccessKeySecret)
 
-	if query.Get("OSSAccessKeyId") != "" {
+	if urlSignature {
 		query.Set("Signature", signature)
 	} else {
 		headers.Set("Authorization", "OSS "+client.AccessKeyId+":"+signature)
diff --git a/pvtz/client.go b/pvtz/client.go
index 8de6636..96ff158 100644
--- a/pvtz/client.go
+++ b/pvtz/client.go
@@ -60,6 +60,16 @@ func NewPVTZClientWithSecurityToken(accessKeyId string, accessKeySecret string,
 	return NewPVTZClientWithEndpointAndSecurityToken(endpoint, accessKeyId, accessKeySecret, securityToken, regionID)
 }
 
+//Onlyfor hangzhou
+func NewPVTZClientWithSecurityToken4RegionalDomain(accessKeyId string, accessKeySecret string, securityToken string, regionID common.Region) *Client {
+	endpoint := os.Getenv("PVTZ_ENDPOINT")
+	if endpoint == "" {
+		endpoint = PVTZDefaultEndpoint
+	}
+
+	return NewPVTZClientWithEndpointAndSecurityToken4RegionalDomain(endpoint, accessKeyId, accessKeySecret, securityToken, regionID)
+}
+
 func NewPVTZClientWithEndpoint(endpoint string, accessKeyId string, accessKeySecret string, regionID common.Region) *Client {
 	return NewPVTZClientWithEndpointAndSecurityToken(endpoint, accessKeyId, accessKeySecret, "", regionID)
 }
@@ -76,3 +86,17 @@ func NewPVTZClientWithEndpointAndSecurityToken(endpoint string, accessKeyId stri
 		InitClient()
 	return client
 }
+
+//only for hangzhou
+func NewPVTZClientWithEndpointAndSecurityToken4RegionalDomain(endpoint string, accessKeyId string, accessKeySecret string, securityToken string, regionID common.Region) *Client {
+	client := &Client{}
+	client.WithEndpoint(endpoint).
+		WithVersion(PVTZAPIVersion).
+		WithAccessKeyId(accessKeyId).
+		WithAccessKeySecret(accessKeySecret).
+		WithSecurityToken(securityToken).
+		WithServiceCode(PVTZServiceCode).
+		WithRegionID(regionID).
+		InitClient4RegionalDomain()
+	return client
+}
diff --git a/pvtz/client_test.go b/pvtz/client_test.go
index 79e35e8..c33ac96 100644
--- a/pvtz/client_test.go
+++ b/pvtz/client_test.go
@@ -43,7 +43,7 @@ func TestAddZone(t *testing.T) {
 	err = client.BindZoneVpc(&BindZoneVpcArgs{
 		ZoneId: zoneId,
 		Vpcs: []VPCType{
-			VPCType{
+			{
 				RegionId: common.Beijing,
 				VpcId:    TestVPCId,
 			},
@@ -115,7 +115,7 @@ func testDescribeZoneRecords(t *testing.T, zoneId string) {
 }
 
 func TestDescribeChangeLogs(t *testing.T) {
-	client := NewTestClient()
+	client := NewTestLocationClientForDebug()
 
 	changeLogs, err := client.DescribeChangeLogs(&DescribeChangeLogsArgs{})
 
diff --git a/pvtz/config_test.go b/pvtz/config_test.go
index 92c7fff..965e49e 100644
--- a/pvtz/config_test.go
+++ b/pvtz/config_test.go
@@ -1,6 +1,7 @@
 package pvtz
 
 import (
+	"github.com/denverdino/aliyungo/common"
 	"os"
 )
 
@@ -10,6 +11,8 @@ var (
 	TestAccessKeyId     = os.Getenv("AccessKeyId")
 	TestAccessKeySecret = os.Getenv("AccessKeySecret")
 	TestVPCId           = os.Getenv("VPCId")
+	TestSecurityToken   = os.Getenv("SecurityToken")
+	TestRegionID        = common.Region(os.Getenv("RegionId"))
 )
 
 var testClient *Client
@@ -30,3 +33,13 @@ func NewTestClientForDebug() *Client {
 	}
 	return testDebugClient
 }
+
+var testDebugLocationClient *Client
+
+func NewTestLocationClientForDebug() *Client {
+	if testDebugLocationClient == nil {
+		testDebugLocationClient = NewPVTZClientWithSecurityToken4RegionalDomain(TestAccessKeyId, TestAccessKeySecret, TestSecurityToken, TestRegionID)
+		testDebugLocationClient.SetDebug(true)
+	}
+	return testDebugLocationClient
+}
diff --git a/pvtz/records.go b/pvtz/records.go
index 80740e7..aab921d 100644
--- a/pvtz/records.go
+++ b/pvtz/records.go
@@ -152,7 +152,7 @@ type UpdateZoneRecordResponse struct {
 // UpdateZoneRecord update zone record
 //
 // You can read doc at https://help.aliyun.com/document_detail/66250.html
-func (client *Client) UpdateZoneRecord(args *AddZoneRecordArgs) (err error) {
+func (client *Client) UpdateZoneRecord(args *UpdateZoneRecordArgs) (err error) {
 	response := &UpdateZoneRecordResponse{}
 
 	err = client.Invoke("UpdateZoneRecord", args, &response)
diff --git a/ram/api.go b/ram/api.go
index d8f5029..be9d400 100644
--- a/ram/api.go
+++ b/ram/api.go
@@ -81,4 +81,7 @@ type RamClientInterface interface {
 	ClearAccountAlias() (RamCommonResponse, error)
 	SetPasswordPolicy(passwordPolicy PasswordPolicyRequest) (PasswordPolicyResponse, error)
 	GetPasswordPolicy() (PasswordPolicyResponse, error)
+
+	// Common Client Methods
+	SetUserAgent(userAgent string)
 }
diff --git a/ram/policy.go b/ram/policy.go
index c1e6de1..aa7aa42 100644
--- a/ram/policy.go
+++ b/ram/policy.go
@@ -24,7 +24,8 @@ type PolicyListResponse struct {
 
 type PolicyResponse struct {
 	RamCommonResponse
-	Policy Policy
+	Policy               Policy
+	DefaultPolicyVersion PolicyVersion
 }
 
 type PolicyQueryRequest struct {
diff --git a/ram/policy_test.go b/ram/policy_test.go
index cb41de6..cbba630 100644
--- a/ram/policy_test.go
+++ b/ram/policy_test.go
@@ -12,7 +12,7 @@ var (
 	policy_name      string
 	policy_document  = PolicyDocument{
 		Statement: []PolicyItem{
-			PolicyItem{
+			{
 				Action:   "*",
 				Effect:   "Allow",
 				Resource: "*",
@@ -203,7 +203,7 @@ func TestListPoliciesForGroup(t *testing.T) {
 	t.Logf("pass ListPoliciesForGroup %++v", resp)
 }
 
-func TEstListEntitiesForPolicy(t *testing.T) {
+func TestListEntitiesForPolicy(t *testing.T) {
 	client := NewTestClient()
 	policyReq := PolicyRequest{
 		PolicyType: "Custom",
diff --git a/ram/role_test.go b/ram/role_test.go
index 8e7912f..28341e1 100644
--- a/ram/role_test.go
+++ b/ram/role_test.go
@@ -20,13 +20,13 @@ var (
 
 	policyDocument = AssumeRolePolicyDocument{
 		Statement: []AssumeRolePolicyItem{
-			AssumeRolePolicyItem{Action: "sts:AssumeRole", Effect: "Allow", Principal: princpal},
+			{Action: "sts:AssumeRole", Effect: "Allow", Principal: princpal},
 		},
 		Version: "1"}
 
 	newPolicyDocument = AssumeRolePolicyDocument{
 		Statement: []AssumeRolePolicyItem{
-			AssumeRolePolicyItem{Action: "sts:AssumeRole", Effect: "Deny", Principal: princpal},
+			{Action: "sts:AssumeRole", Effect: "Deny", Principal: princpal},
 		},
 		Version: "1"}
 
diff --git a/rds/instances.go b/rds/instances.go
index 4479124..fbc2c96 100644
--- a/rds/instances.go
+++ b/rds/instances.go
@@ -205,7 +205,6 @@ type CreateOrderResponse struct {
 }
 
 // CreateOrder create db instance order
-// you can read doc at http://docs.alibaba-inc.com/pages/viewpage.action?pageId=259349053
 func (client *Client) CreateOrder(args *CreateOrderArgs) (resp CreateOrderResponse, err error) {
 	response := CreateOrderResponse{}
 	err = client.Invoke("CreateOrder", args, &response)
diff --git a/rds/instances_test.go b/rds/instances_test.go
index 5fe92e3..8a8292e 100644
--- a/rds/instances_test.go
+++ b/rds/instances_test.go
@@ -254,7 +254,7 @@ func TestAccountAllocatePublicConnection(t *testing.T) {
 	args := AllocateInstancePublicConnectionArgs{
 		DBInstanceId:           DBInstanceId,
 		ConnectionStringPrefix: DBInstanceId + "o",
-		Port: "3306",
+		Port:                   "3306",
 	}
 
 	_, err := client.AllocateInstancePublicConnection(&args)
diff --git a/ros/client.go b/ros/client.go
index 3ecf6ed..b28544b 100755
--- a/ros/client.go
+++ b/ros/client.go
@@ -86,6 +86,14 @@ func (client *Client) SetSecurityToken(securityToken string) {
 	client.SecurityToken = securityToken
 }
 
+// SetTransport sets transport to the http client
+func (client *Client) SetTransport(transport http.RoundTripper) {
+	if client.httpClient == nil {
+		client.httpClient = &http.Client{}
+	}
+	client.httpClient.Transport = transport
+}
+
 type Request struct {
 	Method          string
 	URL             string
@@ -144,6 +152,7 @@ func (client *Client) Invoke(region common.Region, method string, path string, q
 	httpReq.Header.Set("Date", util.GetGMTime())
 	httpReq.Header.Set("Accept", "application/json")
 	//httpReq.Header.Set("x-acs-version", client.Version)
+	httpReq.Header["x-acs-version"] = []string{client.Version}
 
 	httpReq.Header["x-acs-signature-version"] = []string{"1.0"}
 	httpReq.Header["x-acs-signature-nonce"] = []string{util.CreateRandomString()}
@@ -180,13 +189,13 @@ func (client *Client) Invoke(region common.Region, method string, path string, q
 
 	if client.debug {
 		var prettyJSON bytes.Buffer
-		err = json.Indent(&prettyJSON, body, "", "    ")
-		log.Println(string(prettyJSON.Bytes()))
+		_ = json.Indent(&prettyJSON, body, "", "    ")
+		log.Println(prettyJSON.String())
 	}
 
 	if statusCode >= 400 && statusCode <= 599 {
 		errorResponse := common.ErrorResponse{}
-		err = json.Unmarshal(body, &errorResponse)
+		_ = json.Unmarshal(body, &errorResponse)
 		cErr := &common.Error{
 			ErrorResponse: errorResponse,
 			StatusCode:    statusCode,
diff --git a/ros/config_test.go b/ros/config_test.go
index 0f60535..5f2c917 100755
--- a/ros/config_test.go
+++ b/ros/config_test.go
@@ -14,8 +14,8 @@ var (
 	TestSecurityToken   = os.Getenv("SecurityToken")
 	TestRegionID        = common.Region(os.Getenv("RegionId"))
 
-	clientForTestCase      = NewTestClient()
-	debugClientForTestCase = NewTestClientForDebug()
+	debugClientForTestCase    = NewTestClientForDebug()
+	debugRpcClientForTestCase = NewTestRpcClientForDebug()
 )
 
 var testClient *Client
@@ -29,6 +29,7 @@ func NewTestClient() *Client {
 }
 
 var testDebugClient *Client
+var testDebugRpcClient *RpcClient
 
 func NewTestClientForDebug() *Client {
 	if testDebugClient == nil {
@@ -38,3 +39,12 @@ func NewTestClientForDebug() *Client {
 	testDebugClient.SetSecurityToken(TestSecurityToken)
 	return testDebugClient
 }
+
+func NewTestRpcClientForDebug() *RpcClient {
+	if testDebugRpcClient == nil {
+		testDebugRpcClient = NewRpcClient(TestAccessKeyId, TestAccessKeySecret)
+		testDebugRpcClient.SetDebug(true)
+	}
+	testDebugRpcClient.SetSecurityToken(TestSecurityToken)
+	return testDebugRpcClient
+}
diff --git a/ros/other.go b/ros/other.go
index 78ade96..78ea27c 100755
--- a/ros/other.go
+++ b/ros/other.go
@@ -18,6 +18,7 @@ type DescribeEventsRequest struct {
 }
 
 type Event struct {
+	Status             string
 	ResourceStatus     string
 	ResourceName       string
 	StatusReason       string
diff --git a/ros/resource.go b/ros/resource.go
index 88607b4..95638d8 100755
--- a/ros/resource.go
+++ b/ros/resource.go
@@ -40,6 +40,17 @@ func (client *Client) DescribeResource(stackId, stackName, resourceName string)
 	return response, nil
 }
 
+//https://help.aliyun.com/document_detail/28917.html?spm=5176.doc28916.6.589.BUPJqx
+func (client *Client) DescribeResourceByRegion(regionId common.Region, stackId, stackName, resourceName string) (*Resource, error) {
+	response := &Resource{}
+	err := client.Invoke(regionId, http.MethodGet, fmt.Sprintf("/stacks/%s/%s/resources/%s", stackName, stackId, resourceName), nil, nil, &response)
+	if err != nil {
+		return nil, err
+	}
+
+	return response, nil
+}
+
 //https://help.aliyun.com/document_detail/28918.html?spm=5176.doc28917.6.590.smknll
 type SupportStatus string
 
diff --git a/ros/rpc_client.go b/ros/rpc_client.go
new file mode 100644
index 0000000..3122b16
--- /dev/null
+++ b/ros/rpc_client.go
@@ -0,0 +1,51 @@
+package ros
+
+import (
+	"github.com/denverdino/aliyungo/common"
+	"os"
+)
+
+const (
+	ROS_RPC_APIVersion = "2019-09-10"
+	ROSServiceCode     = "ros"
+)
+
+type RpcClient struct {
+	common.Client
+}
+
+func NewRpcClient(accessKeyId, accessKeySecret string) *RpcClient {
+	endpoint := os.Getenv("ROS_ENDPOINT")
+	if endpoint == "" {
+		endpoint = ROSDefaultEndpoint
+	}
+	return NewRpcClientWithEndpoint(endpoint, accessKeyId, accessKeySecret)
+}
+
+func NewRpcClientWithEndpoint(endpoint string, accessKeyId string, accessKeySecret string) *RpcClient {
+	client := &RpcClient{}
+	client.Init(endpoint, ROS_RPC_APIVersion, accessKeyId, accessKeySecret)
+	return client
+}
+
+func NewRosRpcClientWithSecurityToken(accessKeyId string, accessKeySecret string, securityToken string, regionID common.Region) *RpcClient {
+	endpoint := os.Getenv("ROS_ENDPOINT")
+	if endpoint == "" {
+		endpoint = ROSDefaultEndpoint
+	}
+
+	return NewRosRpcClientWithEndpointAndSecurityToken(endpoint, accessKeyId, accessKeySecret, securityToken, regionID)
+}
+
+func NewRosRpcClientWithEndpointAndSecurityToken(endpoint string, accessKeyId string, accessKeySecret string, securityToken string, regionID common.Region) *RpcClient {
+	client := &RpcClient{}
+	client.WithEndpoint(endpoint).
+		WithVersion(ROS_RPC_APIVersion).
+		WithAccessKeyId(accessKeyId).
+		WithAccessKeySecret(accessKeySecret).
+		WithSecurityToken(securityToken).
+		WithServiceCode(ROSServiceCode).
+		WithRegionID(regionID).
+		InitClient()
+	return client
+}
diff --git a/ros/signature.go b/ros/signature.go
index c9d9c34..4a94cd6 100755
--- a/ros/signature.go
+++ b/ros/signature.go
@@ -22,8 +22,9 @@ func (client *Client) signRequest(request *http.Request) {
 	_, canonicalizedHeader := canonicalizeHeader(headers)
 
 	stringToSign := request.Method + "\n" + accept + "\n" + contentMd5 + "\n" + contentType + "\n" + date + "\n" + canonicalizedHeader + canonicalizedResource
-
-	log.Println("stringToSign: ", stringToSign)
+	if client.debug {
+		log.Println("stringToSign: ", stringToSign)
+	}
 	signature := util.CreateSignature(stringToSign, client.AccessKeySecret)
 	headers.Set("Authorization", "acs "+client.AccessKeyId+":"+signature)
 }
diff --git a/ros/stack.go b/ros/stack.go
index a329631..91f2987 100755
--- a/ros/stack.go
+++ b/ros/stack.go
@@ -9,13 +9,21 @@ import (
 	"github.com/denverdino/aliyungo/util"
 )
 
+type DeletionProtection string
+
+const (
+	DeletionProtectionEnabled  = DeletionProtection("Enabled")
+	DeletionProtectionDisabled = DeletionProtection("Disabled")
+)
+
 //https://help.aliyun.com/document_detail/28910.html?spm=5176.doc50083.6.580.b5wkQr
 type CreateStackRequest struct {
-	Name            string
-	Template        string
-	Parameters      interface{}
-	DisableRollback bool
-	TimeoutMins     int
+	Name               string
+	Template           string
+	Parameters         interface{}
+	DisableRollback    bool
+	DeletionProtection DeletionProtection
+	TimeoutMins        int
 }
 
 type CreateStackResponse struct {
@@ -38,6 +46,19 @@ type DeleteStackRequest struct {
 	RegionId common.Region
 }
 
+type DeleteStackRpcRequest struct {
+	RegionId           common.Region
+	StackId            string
+	RetainResources    common.FlattenArray
+	RetainAllResources bool
+}
+
+type SetDeletionProtectionRequest struct {
+	RegionId           common.Region
+	StackId            string
+	DeletionProtection DeletionProtection
+}
+
 type DeleteStackResponse struct {
 	common.Response
 }
@@ -203,10 +224,13 @@ func (client *Client) PreviewStack(regionId common.Region, args PreviewStackRequ
 
 //https://help.aliyun.com/document_detail/49066.html?spm=5176.doc28910.6.586.MJjWQh
 type UpdateStackRequest struct {
-	Template        string
-	Parameters      interface{}
-	DisableRollback bool
-	TimeoutMins     int
+	Template          string
+	Parameters        interface{}
+	DisableRollback   bool
+	TimeoutMins       int
+	StackPolicy       interface{}
+	UpdateAllowPolicy interface{}
+	Existing          bool
 }
 
 type UpdateStackResponse struct {
@@ -224,3 +248,23 @@ func (client *Client) UpdateStack(regionId common.Region, stackId string, stackN
 
 	return stack, nil
 }
+
+// rpc api: https://help.aliyun.com/document_detail/132113.html?spm=a2c4g.11174283.6.880.1b1d143eCzdE0b
+func (client *RpcClient) DeleteStack(args *DeleteStackRpcRequest) (*DeleteStackResponse, error) {
+	response := &DeleteStackResponse{}
+	err := client.InvokeByFlattenMethod("DeleteStack", args, response)
+	if err != nil {
+		return nil, err
+	}
+	return response, nil
+}
+
+// rpc api: https://help.aliyun.com/document_detail/161560.html?spm=a2c4g.11186623.6.991.18f62842yOfz9G
+func (client *RpcClient) SetDeletionProtection(args *SetDeletionProtectionRequest) (*common.Response, error) {
+	response := &common.Response{}
+	err := client.Invoke("SetDeletionProtection", args, response)
+	if err != nil {
+		return nil, err
+	}
+	return response, nil
+}
diff --git a/ros/stack_test.go b/ros/stack_test.go
index 74a9114..6e8dc71 100755
--- a/ros/stack_test.go
+++ b/ros/stack_test.go
@@ -1,6 +1,7 @@
 package ros
 
 import (
+	"strings"
 	"testing"
 
 	"fmt"
@@ -91,6 +92,24 @@ func TestClient_CreateStack(t *testing.T) {
 	}
 }
 
+func TestClient_CreateStack_DeletionProtection(t *testing.T) {
+	args := &CreateStackRequest{
+		Name:               fmt.Sprintf("my-k8s-test-stack-%d", time.Now().Unix()),
+		Template:           myTestTemplate,
+		Parameters:         map[string]interface{}{},
+		DisableRollback:    false,
+		DeletionProtection: DeletionProtectionEnabled,
+		TimeoutMins:        30,
+	}
+
+	response, err := debugClientForTestCase.CreateStack(TestRegionID, args)
+	if err != nil {
+		t.Fatalf("Failed to CreateStack %++v", err)
+	} else {
+		t.Logf("Success %++v", response)
+	}
+}
+
 func TestClient_DeleteStack(t *testing.T) {
 	stackName := os.Getenv("StackName")
 	stackId := os.Getenv("StackId")
@@ -159,3 +178,50 @@ func TestClient_UpdateStack(t *testing.T) {
 		t.Logf("Success %++v", response)
 	}
 }
+
+func TestClient_DeleteStackWithParams(t *testing.T) {
+	stackId := os.Getenv("StackId")
+	resourceIds := os.Getenv("RetainResources")
+
+	args := &DeleteStackRpcRequest{
+		RegionId:        TestRegionID,
+		StackId:         stackId,
+		RetainResources: strings.Split(resourceIds, ","),
+	}
+
+	response, err := debugRpcClientForTestCase.DeleteStack(args)
+	if err != nil {
+		t.Fatalf("Failed to DeleteStack %++v", err)
+	} else {
+		t.Logf("Success %++v", response)
+	}
+}
+
+func TestClient_DeleteStack_DisabledProtection(t *testing.T) {
+	stackId := os.Getenv("StackId")
+
+	args := &SetDeletionProtectionRequest{
+		RegionId:           TestRegionID,
+		StackId:            stackId,
+		DeletionProtection: DeletionProtectionDisabled,
+	}
+
+	response, err := debugRpcClientForTestCase.SetDeletionProtection(args)
+	if err != nil {
+		t.Fatalf("Failed to SetDeletionProtection %++v", err)
+	} else {
+		t.Logf("Success %++v", response)
+	}
+
+	deleteArgs := &DeleteStackRpcRequest{
+		RegionId: TestRegionID,
+		StackId:  stackId,
+	}
+
+	resp, err := debugRpcClientForTestCase.DeleteStack(deleteArgs)
+	if err != nil {
+		t.Fatalf("Failed to DeleteStack %++v", err)
+	} else {
+		t.Logf("Success %++v", resp)
+	}
+}
diff --git a/ros/standard/client.go b/ros/standard/client.go
new file mode 100644
index 0000000..0889b92
--- /dev/null
+++ b/ros/standard/client.go
@@ -0,0 +1,48 @@
+package standard
+
+import (
+	"github.com/denverdino/aliyungo/common"
+
+	"os"
+)
+
+type Client struct {
+	common.Client
+}
+
+const (
+	// ROSDefaultEndpoint is the default API endpoint of ESS services
+	ROSDefaultEndpoint = "https://ros.aliyuncs.com"
+	ROSAPIVersion      = "2019-09-10"
+	ROSServiceCode     = "ros"
+)
+
+// NewClient creates a new instance of RDS client
+func NewClient(accessKeyId, accessKeySecret string) *Client {
+	endpoint := os.Getenv("ROS_ENDPOINT")
+	if endpoint == "" {
+		endpoint = ROSDefaultEndpoint
+	}
+	return NewClientWithEndpoint(endpoint, accessKeyId, accessKeySecret)
+}
+
+func NewClientWithEndpoint(endpoint string, accessKeyId, accessKeySecret string) *Client {
+	client := &Client{}
+	client.Init(endpoint, ROSAPIVersion, accessKeyId, accessKeySecret)
+	return client
+}
+
+func NewROSClient(accessKeyId, accessKeySecret string, regionID common.Region) *Client {
+	endpoint := os.Getenv("ROS_ENDPOINT")
+	if endpoint == "" {
+		endpoint = ROSDefaultEndpoint
+	}
+
+	return NewClientWithRegion(endpoint, accessKeyId, accessKeySecret, regionID)
+}
+
+func NewClientWithRegion(endpoint string, accessKeyId, accessKeySecret string, regionID common.Region) *Client {
+	client := &Client{}
+	client.NewInit(endpoint, ROSAPIVersion, accessKeyId, accessKeySecret, ROSServiceCode, regionID)
+	return client
+}
diff --git a/ros/standard/client_test.go b/ros/standard/client_test.go
new file mode 100644
index 0000000..454e1e5
--- /dev/null
+++ b/ros/standard/client_test.go
@@ -0,0 +1,77 @@
+package standard
+
+import (
+	"fmt"
+	"github.com/denverdino/aliyungo/common"
+	"testing"
+)
+
+const (
+	TestAccessKeyId     = ""
+	TestAccessKeySecret = ""
+	Region              = common.Shanghai
+	StackID             = "c7f1fed9-0104-4596-aae6-aa5215f5a793"
+)
+
+func NewTestClient() *Client {
+	return NewROSClient(TestAccessKeyId, TestAccessKeySecret, Region)
+}
+
+func TestGetStack(t *testing.T) {
+
+	client := NewTestClient()
+	req := GetStackRequest{
+		RegionId: Region,
+		StackId:  StackID,
+	}
+	res, err := client.GetStack(&req)
+	if err != nil {
+		t.Fail()
+	}
+	fmt.Printf("Response: %+v\n", res)
+}
+
+func TestListStack(t *testing.T) {
+	client := NewTestClient()
+	req := &ListStacksRequest{}
+	res, err := client.ListStacks(req)
+	if err != nil {
+		t.Fail()
+	}
+	fmt.Printf("ListResponse: %+v\n", res)
+}
+
+func TestListStackEvent(t *testing.T) {
+	client := NewTestClient()
+	req := &ListStackEventsRequest{
+		StackId: StackID,
+	}
+	res, err := client.ListStackEvents(req)
+	if err != nil {
+		t.Fail()
+	}
+	fmt.Printf("ListEventResponse: %+v\n", res)
+}
+
+func TestCreateStack(t *testing.T) {
+	client := NewTestClient()
+	req := &CreateStackRequest{
+		StackName:        "TDDDDDDD",
+		TemplateBody:     tpl,
+		DisableRollback:  true,
+		TimeoutInMinutes: 60,
+		Parameters: []Parameter{
+			{ParameterKey: "SystemDisk", ParameterValue: ""},
+		},
+	}
+	res, err := client.CreateStack(req)
+	if err != nil {
+		t.Logf("create stack: %s", err.Error())
+		t.Fail()
+	}
+	fmt.Printf("ListEventResponse: %+v\n", res)
+}
+
+var tpl = `
+
+`
diff --git a/ros/standard/stack.go b/ros/standard/stack.go
new file mode 100755
index 0000000..365f6d8
--- /dev/null
+++ b/ros/standard/stack.go
@@ -0,0 +1,316 @@
+package standard
+
+import (
+	"github.com/denverdino/aliyungo/common"
+)
+
+type DeletionProtection string
+
+const (
+	DeletionProtectionEnabled  = DeletionProtection("Enabled")
+	DeletionProtectionDisabled = DeletionProtection("Disabled")
+)
+
+//https://help.aliyun.com/document_detail/28910.html?spm=5176.doc50083.6.580.b5wkQr
+type CreateStackRequest struct {
+	RegionId           common.Region
+	StackName          string
+	DisableRollback    bool
+	TemplateBody       string
+	TemplateURL        string
+	Parameters         []Parameter
+	StackPolicyURL     string
+	TimeoutInMinutes   int
+	StackPolicyBody    string
+	ClientToken        string
+	NotificationURLs   []string
+	DeletionProtection DeletionProtection
+	RamRoleName        string
+}
+
+type CreateStackResponse struct {
+	StackId string
+	common.Response
+}
+
+type ListStackEventsRequest struct {
+	common.Pagination
+	RegionId          common.Region
+	StackId           string
+	Status            []string
+	ResourceType      []string
+	LogicalResourceId []string
+}
+
+type ListStackEventsResponse struct {
+	common.Response
+	common.PaginationResult
+	RegionId common.Region
+	Events   []Event
+}
+
+type Event struct {
+	StackId            string
+	Status             string
+	StackName          string
+	StatusReason       string
+	EventId            string
+	LogicalResourceId  string
+	ResourceType       string
+	PhysicalResourceId string
+	CreateTime         string
+}
+
+func (client *Client) ListStackEvents(args *ListStackEventsRequest) (*ListStackEventsResponse, error) {
+	response := &ListStackEventsResponse{}
+	err := client.Invoke("ListStackEvents", args, response)
+	if err != nil {
+		return nil, err
+	}
+
+	return response, nil
+}
+
+func (client *Client) CreateStack(args *CreateStackRequest) (*CreateStackResponse, error) {
+	stack := &CreateStackResponse{}
+	err := client.Invoke("CreateStack", args, stack)
+	if err != nil {
+		return nil, err
+	}
+
+	return stack, nil
+}
+
+//https://help.aliyun.com/document_detail/28911.html?spm=5176.doc28910.6.581.etoi2Z
+type DeleteStackRequest struct {
+	RegionId           common.Region
+	StackId            string
+	RetainAllResources bool
+	RetainResources    []string
+	RamRoleName        string
+}
+
+type DeleteStackResponse struct {
+	common.Response
+}
+
+func (client *Client) DeleteStack(req *DeleteStackRequest) (*DeleteStackResponse, error) {
+	response := &DeleteStackResponse{}
+	err := client.Invoke("DeleteStack", req, response)
+	if err != nil {
+		return nil, err
+	}
+
+	return response, nil
+}
+
+type GetStackRequest struct {
+	RegionId    common.Region
+	StackId     string
+	ClientToken string
+}
+
+type GetStackResponse struct {
+	CreateTime          string
+	Description         string
+	DisableRollback     bool
+	NotificationURLs    []string
+	Outputs             []Output
+	ParentStackId       string
+	RegionId            common.Region
+	Status              string
+	StackId             string
+	StackName           string
+	Parameters          []Parameter
+	UpdateTime          string
+	StatusReason        string
+	TemplateDescription string
+	TimeoutInMinutes    int
+
+	RequestId          string
+	DeletionProtection DeletionProtection
+	DriftDetectionTime string
+	RamRoleName        string
+	RootStackId        string
+	StackDriftStatus   string
+	StackType          string
+}
+
+type Parameter struct {
+	ParameterKey   string
+	ParameterValue string
+}
+
+type Output struct {
+	Description string
+	OutputKey   string
+	OutputValue interface{}
+}
+
+func (client *Client) GetStack(req *GetStackRequest) (*GetStackResponse, error) {
+	response := &GetStackResponse{}
+	err := client.Invoke("GetStack", req, response)
+	if err != nil {
+		return nil, err
+	}
+
+	return response, nil
+}
+
+type ListStacksRequest struct {
+	RegionId        common.Region
+	StackId         string
+	Status          []string
+	ParentStackId   string
+	StackName       []string
+	ShowNestedStack bool
+	Tag             []Tag
+	common.Pagination
+}
+
+type ListStacksResponse struct {
+	common.PaginationResult
+	common.Response
+	Stacks []Stack
+}
+
+type Stack struct {
+	CreateTime string
+
+	DisableRollback    bool
+	DriftDetectionTime string
+	ParentStackId      string
+	RegionId           common.Region
+	StackDriftStatus   string
+	StackId            string
+	StackName          string
+	Status             string
+	StatusReason       string
+
+	TimeoutInMinutes int
+	UpdateTime       string
+}
+
+type Tag struct {
+	Key   string
+	Value string
+}
+
+func (client *Client) ListStacks(req *ListStacksRequest) (*ListStacksResponse, error) {
+	response := &ListStacksResponse{}
+	err := client.Invoke("ListStacks", req, response)
+	if err != nil {
+		return nil, err
+	}
+
+	return response, nil
+}
+
+type UpdateStackRequest struct {
+	Parameters                  []Parameter
+	RegionId                    string
+	StackId                     string
+	ClientToken                 string
+	StackPolicyDuringUpdateBody string
+	TimeoutInMinutes            int
+	TemplateBody                string
+	StackPolicyURL              string
+	StackPolicyDuringUpdateURL  string
+	StackPolicyBody             string
+	UsePreviousParameters       bool
+	DisableRollback             bool
+	TemplateURL                 string
+	RamRoleName                 string
+	ReplacementOption           string
+}
+
+type UpdateStackResponse struct {
+	StackId string
+	common.Response
+}
+
+func (client *Client) UpdateStack(req *UpdateStackRequest) (*UpdateStackResponse, error) {
+	response := &UpdateStackResponse{}
+	err := client.Invoke("UpdateStack", req, response)
+	if err != nil {
+		return nil, err
+	}
+
+	return response, nil
+}
+
+type GetStackResourceRequest struct {
+	StackId                string
+	LogicalResourceId      string
+	ClientToken            string
+	ShowResourceAttributes bool
+	RegionId               common.Region
+}
+
+type GetStackResourceResponse struct {
+	Status            string
+	Description       string
+	LogicalResourceId string
+	StackId           string
+
+	StackName           string
+	StatusReason        string
+	PhysicalResourceId  string
+	ResourceType        string
+	CreateTime          string
+	Metadata            map[string]string
+	UpdateTime          string
+	ResourceAttributes  []ResourceAttribute
+	RequestId           string
+	DriftDetectionTime  string
+	ResourceDriftStatus string
+}
+type ResourceAttribute struct {
+	ResourceAttributeValue string
+	ResourceAttributeKey   string
+}
+
+func (client *Client) GetStackResource(req *GetStackResourceRequest) (*GetStackResourceResponse, error) {
+	response := &GetStackResourceResponse{}
+	err := client.Invoke("GetStackResource", req, response)
+	if err != nil {
+		return nil, err
+	}
+
+	return response, nil
+}
+
+type ListStackResourcesRequest struct {
+	RegionId common.Region
+	StackId  string
+}
+
+type ListStackResourcesResponse struct {
+	common.Response
+	Resources []Resource
+}
+
+type Resource struct {
+	CreateTime         string
+	DriftDetectionTime string
+	LogicalResourceId  string
+	PhysicalResourceId string
+
+	ResourceDriftStatus string
+	ResourceType        string
+	StackId             string
+	StackName           string
+	Status              string
+	StatusReason        string
+	UpdateTime          string
+}
+
+func (client *Client) ListStackResources(req *ListStackResourcesRequest) (*ListStackResourcesResponse, error) {
+	response := &ListStackResourcesResponse{}
+	err := client.Invoke("ListStackResources", req, response)
+	if err != nil {
+		return nil, err
+	}
+
+	return response, nil
+}
diff --git a/slb/client.go b/slb/client.go
index d66ceb2..4b43cbc 100644
--- a/slb/client.go
+++ b/slb/client.go
@@ -57,6 +57,16 @@ func NewSLBClientWithSecurityToken(accessKeyId string, accessKeySecret string, s
 	return NewSLBClientWithEndpointAndSecurityToken(endpoint, accessKeyId, accessKeySecret, securityToken, regionID)
 }
 
+//Only for hangzhou
+func NewSLBClientWithSecurityToken4RegionalDomain(accessKeyId string, accessKeySecret string, securityToken string, regionID common.Region) *Client {
+	endpoint := os.Getenv("SLB_ENDPOINT")
+	if endpoint != "" {
+		return NewSLBClientWithEndpointAndSecurityToken(endpoint, accessKeyId, accessKeySecret, securityToken, regionID)
+	}
+
+	return NewSLBClientWithEndpointAndSecurityToken4RegionalDomain(endpoint, accessKeyId, accessKeySecret, securityToken, regionID)
+}
+
 func NewSLBClientWithEndpointAndSecurityToken(endpoint string, accessKeyId string, accessKeySecret string, securityToken string, regionID common.Region) *Client {
 	client := &Client{}
 	client.WithEndpoint(endpoint).
@@ -69,3 +79,17 @@ func NewSLBClientWithEndpointAndSecurityToken(endpoint string, accessKeyId strin
 		InitClient()
 	return client
 }
+
+//only for hangzhou
+func NewSLBClientWithEndpointAndSecurityToken4RegionalDomain(endpoint string, accessKeyId string, accessKeySecret string, securityToken string, regionID common.Region) *Client {
+	client := &Client{}
+	client.WithEndpoint(endpoint).
+		WithVersion(SLBAPIVersion).
+		WithAccessKeyId(accessKeyId).
+		WithAccessKeySecret(accessKeySecret).
+		WithSecurityToken(securityToken).
+		WithServiceCode(SLBServiceCode).
+		WithRegionID(regionID).
+		InitClient4RegionalDomain()
+	return client
+}
diff --git a/slb/config_test.go b/slb/config_test.go
index e0b3668..37e2fad 100644
--- a/slb/config_test.go
+++ b/slb/config_test.go
@@ -16,6 +16,7 @@ var (
 	TestVServerGroupID  = "MY_VSERVER_GROUPID"
 	TestListenerPort    = 9000
 	TestInstanceId      = "MY_INSTANCE_ID"
+	TestENIId           = "MY_ENI_ID"
 	TestRegionID        = common.Region(os.Getenv("RegionId"))
 	TestIAmRich         = false
 	TestQuick           = false
@@ -44,7 +45,7 @@ var testDebugNewSLBClient *Client
 
 func NewTestNewSLBClientForDebug() *Client {
 	if testDebugNewSLBClient == nil {
-		testDebugNewSLBClient = NewSLBClientWithSecurityToken(TestAccessKeyId, TestAccessKeySecret, TestSecurityToken, TestRegionID)
+		testDebugNewSLBClient = NewSLBClientWithSecurityToken4RegionalDomain(TestAccessKeyId, TestAccessKeySecret, TestSecurityToken, TestRegionID)
 		testDebugNewSLBClient.SetDebug(true)
 	}
 	return testDebugNewSLBClient
diff --git a/slb/listeners.go b/slb/listeners.go
index 9819025..eebf41b 100644
--- a/slb/listeners.go
+++ b/slb/listeners.go
@@ -99,6 +99,8 @@ type HTTPListenerType struct {
 	UnhealthyThreshold     int
 	HealthCheckTimeout     int
 	HealthCheckInterval    int
+	ForwardPort            int
+	ListenerForward        FlagType
 	HealthCheckHttpCode    HealthCheckHttpCodeType
 	VServerGroup           FlagType
 	VServerGroupId         string
@@ -106,6 +108,10 @@ type HTTPListenerType struct {
 	XForwardedFor_SLBID    FlagType
 	XForwardedFor_SLBIP    FlagType
 	XForwardedFor_proto    FlagType
+	Description            string
+	AclId                  string
+	AclStatus              string
+	AclType                string
 }
 type CreateLoadBalancerHTTPListenerArgs HTTPListenerType
 
@@ -147,7 +153,7 @@ type TCPListenerType struct {
 	BackendServerPort         int
 	Bandwidth                 int
 	Scheduler                 SchedulerType
-	PersistenceTimeout        int
+	PersistenceTimeout        *int
 	HealthCheck               FlagType
 	HealthCheckType           HealthCheckType
 	HealthCheckDomain         string
@@ -160,6 +166,10 @@ type TCPListenerType struct {
 	HealthCheckHttpCode       HealthCheckHttpCodeType
 	VServerGroup              FlagType
 	VServerGroupId            string
+	Description               string
+	AclId                     string
+	AclStatus                 string
+	AclType                   string
 }
 
 type CreateLoadBalancerTCPListenerArgs TCPListenerType
@@ -179,7 +189,7 @@ type UDPListenerType struct {
 	BackendServerPort         int
 	Bandwidth                 int
 	Scheduler                 SchedulerType
-	PersistenceTimeout        int
+	PersistenceTimeout        *int
 	HealthCheck               FlagType
 	HealthCheckConnectPort    int
 	HealthyThreshold          int
@@ -188,6 +198,11 @@ type UDPListenerType struct {
 	HealthCheckInterval       int
 	VServerGroup              FlagType
 	VServerGroupId            string
+	Description               string
+
+	AclId     string
+	AclStatus string
+	AclType   string
 }
 type CreateLoadBalancerUDPListenerArgs UDPListenerType
 
diff --git a/slb/listerners_test.go b/slb/listerners_test.go
index 6904f9f..f1747ba 100644
--- a/slb/listerners_test.go
+++ b/slb/listerners_test.go
@@ -1,6 +1,11 @@
 package slb
 
-import "testing"
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"testing"
+)
 
 func testListeners(t *testing.T, client *Client, loadBalancerId string) {
 
@@ -41,3 +46,38 @@ func testListeners(t *testing.T, client *Client, loadBalancerId string) {
 	}
 	t.Logf("Listener: %++v", *response)
 }
+
+func TestDescribeListener(t *testing.T) {
+
+	response, err := client.DescribeLoadBalancerTCPListenerAttribute(loadBalancerId, 22)
+	if err != nil {
+		t.Error(err)
+	} else {
+		fmt.Println(PrettyJson(response))
+	}
+}
+
+func TestDescribeSLB(t *testing.T) {
+
+	response, err := client.DescribeLoadBalancerAttribute(loadBalancerId)
+	if err != nil {
+		t.Error(err)
+	} else {
+		fmt.Println(PrettyJson(response))
+	}
+}
+
+func PrettyJson(obj interface{}) string {
+	pretty := bytes.Buffer{}
+	data, err := json.Marshal(obj)
+	if err != nil {
+		return err.Error()
+	}
+	err = json.Indent(&pretty, data, "", "    ")
+
+	if err != nil {
+		return err.Error()
+	}
+
+	return pretty.String()
+}
diff --git a/slb/loadbalancers.go b/slb/loadbalancers.go
index 10b1d9f..1bffd71 100644
--- a/slb/loadbalancers.go
+++ b/slb/loadbalancers.go
@@ -22,6 +22,13 @@ const (
 	PayByTraffic   = InternetChargeType("paybytraffic")
 )
 
+type AddressIPVersionType string
+
+const (
+	IPv4 = AddressIPVersionType("ipv4")
+	IPv6 = AddressIPVersionType("ipv6")
+)
+
 type LoadBalancerSpecType string
 
 const (
@@ -33,17 +40,29 @@ const (
 	S3Large  = "slb.s3.large"
 )
 
+type ModificationProtectionType string
+
+const (
+	NonProtection     = ModificationProtectionType("NonProtection")
+	ConsoleProtection = ModificationProtectionType("ConsoleProtection")
+)
+
 type CreateLoadBalancerArgs struct {
-	RegionId           common.Region
-	LoadBalancerName   string
-	AddressType        AddressType
-	VSwitchId          string
-	InternetChargeType InternetChargeType
-	Bandwidth          int
-	ClientToken        string
-	MasterZoneId       string
-	SlaveZoneId        string
-	LoadBalancerSpec   LoadBalancerSpecType
+	RegionId                     common.Region
+	LoadBalancerName             string
+	AddressType                  AddressType
+	VSwitchId                    string
+	InternetChargeType           InternetChargeType
+	Bandwidth                    int
+	ClientToken                  string
+	MasterZoneId                 string
+	SlaveZoneId                  string
+	LoadBalancerSpec             LoadBalancerSpecType
+	AddressIPVersion             AddressIPVersionType
+	DeleteProtection             FlagType
+	ModificationProtectionStatus ModificationProtectionType
+	ModificationProtectionReason string
+	ResourceGroupId              string
 }
 
 type CreateLoadBalancerResponse struct {
@@ -54,6 +73,9 @@ type CreateLoadBalancerResponse struct {
 	VpcId            string
 	VSwitchId        string
 	LoadBalancerName string
+	MasterZoneId     string
+	SlaveZoneId      string
+	AddressIPVersion AddressIPVersionType
 }
 
 // CreateLoadBalancer create loadbalancer
@@ -193,6 +215,7 @@ type DescribeLoadBalancersArgs struct {
 type ListenerPortAndProtocolType struct {
 	ListenerPort     int
 	ListenerProtocol string
+	Description      string
 }
 
 type BackendServerType struct {
@@ -201,22 +224,34 @@ type BackendServerType struct {
 	Type     string
 }
 
+type ServiceManagedModeType string
+
+const (
+	Managed           = ServiceManagedModeType("Managed")
+	Unmanaged         = ServiceManagedModeType("Unmanaged")
+	DependencyManaged = ServiceManagedModeType("DependencyManaged")
+)
+
 type LoadBalancerType struct {
-	LoadBalancerId     string
-	LoadBalancerName   string
-	LoadBalancerStatus string
-	Address            string
-	RegionId           common.Region
-	RegionIdAlias      string
-	AddressType        AddressType
-	VSwitchId          string
-	VpcId              string
-	NetworkType        string
-	Bandwidth          int
-	InternetChargeType InternetChargeType
-	CreateTime         string //Why not ISO 6801
-	CreateTimeStamp    util.ISO6801Time
-	ListenerPorts      struct {
+	LoadBalancerId               string
+	ResourceGroupId              string
+	LoadBalancerName             string
+	LoadBalancerStatus           string
+	Address                      string
+	RegionId                     common.Region
+	RegionIdAlias                string
+	AddressType                  AddressType
+	VSwitchId                    string
+	VpcId                        string
+	NetworkType                  string
+	Bandwidth                    int
+	InternetChargeType           InternetChargeType
+	CreateTime                   string //Why not ISO 6801
+	CreateTimeStamp              util.ISO6801Time
+	DeleteProtection             FlagType
+	ModificationProtectionStatus ModificationProtectionType
+	ModificationProtectionReason string
+	ListenerPorts                struct {
 		ListenerPort []int
 	}
 	ListenerPortsAndProtocol struct {
@@ -225,7 +260,11 @@ type LoadBalancerType struct {
 	BackendServers struct {
 		BackendServer []BackendServerType
 	}
-	LoadBalancerSpec LoadBalancerSpecType
+	LoadBalancerSpec   LoadBalancerSpecType
+	MasterZoneId       string
+	SlaveZoneId        string
+	AddressIPVersion   AddressIPVersionType
+	ServiceManagedMode ServiceManagedModeType
 }
 
 type DescribeLoadBalancersResponse struct {
@@ -321,3 +360,79 @@ func (client *Client) WaitForLoadBalancerAsyn(loadBalancerId string, status Stat
 	}
 	return nil
 }
+
+type SetLoadBalancerDeleteProtectionArgs struct {
+	LoadBalancerId   string
+	DeleteProtection FlagType
+	RegionId         common.Region
+}
+
+type SetLoadBalancerDeleteProtectionResponse struct {
+	common.Response
+}
+
+// SetLoadBalancerDeleteProtection loadbalancer delete protection
+//
+// You can read doc at https://help.aliyun.com/document_detail/122674.html?spm=a2c4g.11186623.6.720.694f4265hwOdXQ
+func (client *Client) SetLoadBalancerDeleteProtection(args *SetLoadBalancerDeleteProtectionArgs) (err error) {
+	response := &SetLoadBalancerDeleteProtectionResponse{}
+	err = client.Invoke("SetLoadBalancerDeleteProtection", args, response)
+	return err
+}
+
+type SetLoadBalancerModificationProtectionArgs struct {
+	RegionId                     common.Region
+	LoadBalancerId               string
+	ModificationProtectionStatus ModificationProtectionType
+	ModificationProtectionReason string
+}
+
+type SetLoadBalancerModificationProtectionResponse struct {
+	common.Response
+}
+
+func (client *Client) SetLoadBalancerModificationProtection(args *SetLoadBalancerModificationProtectionArgs) (err error) {
+	response := &SetLoadBalancerModificationProtectionResponse{}
+	err = client.Invoke("SetLoadBalancerModificationProtection", args, response)
+	return err
+}
+
+type ManagedResourceType string
+
+const (
+	ManagedLoadBalancer           = ManagedResourceType("LoadBalancer")
+	ManagedTls                    = ManagedResourceType("Tls")
+	ManagedVServerGroup           = ManagedResourceType("VServerGroup")
+	ManagedMasterSlaveServerGroup = ManagedResourceType("MasterSlaveServerGroup")
+	ManagedAcl                    = ManagedResourceType("Acl")
+	ManagedListener               = ManagedResourceType("Listener")
+	ManagedRule                   = ManagedResourceType("Rule")
+	ManagedAppRule                = ManagedResourceType("AppRule")
+	ManagedDomainExtension        = ManagedResourceType("DomainExtension")
+)
+
+type ManagedResourceModel struct {
+	ResourceId string
+	Port       int
+	Protocol   string
+}
+
+type ServiceManagedControlArgs struct {
+	RegionId           common.Region
+	ServiceManagedMode ServiceManagedModeType
+	ResourceUid        string
+	ResourceBid        string
+	ResourceType       ManagedResourceType
+	Resources          []ManagedResourceModel
+}
+
+type ServiceManagedControlResponse struct {
+	common.Response
+}
+
+//api: https://yuque.antfin-inc.com/docs/share/63b5a2d3-6fb3-4bd7-a50e-c4b385b866fd?#
+func (client *Client) ServiceManagedControl(args *ServiceManagedControlArgs) (err error) {
+	response := &ServiceManagedControlResponse{}
+	err = client.Invoke("ServiceManagedControl", args, response)
+	return err
+}
diff --git a/slb/loadbalancers_test.go b/slb/loadbalancers_test.go
index 731a7c6..567ad46 100644
--- a/slb/loadbalancers_test.go
+++ b/slb/loadbalancers_test.go
@@ -6,13 +6,14 @@ import (
 	"github.com/denverdino/aliyungo/common"
 )
 
-func TestLoadBlancer(t *testing.T) {
+func TestLoadBalancer(t *testing.T) {
 
 	client := NewTestClientForDebug()
 
 	creationArgs := CreateLoadBalancerArgs{
 		RegionId:         common.Beijing,
 		LoadBalancerName: "test-slb",
+		LoadBalancerSpec: S2Medium, // eni not support slb.s0.share slb(default slb.s0.share)
 		AddressType:      InternetAddressType,
 		ClientToken:      client.GenerateClientToken(),
 	}
@@ -63,6 +64,63 @@ func TestLoadBlancer(t *testing.T) {
 
 }
 
+func TestLoadBalancerIPv6(t *testing.T) {
+
+	client := NewTestClientForDebug()
+
+	creationArgs := CreateLoadBalancerArgs{
+		RegionId:         common.Hangzhou,
+		LoadBalancerName: "test-slb-ipv6",
+		AddressType:      InternetAddressType,
+		MasterZoneId:     "cn-hangzhou-e",
+		SlaveZoneId:      "cn-hangzhou-f",
+		ClientToken:      client.GenerateClientToken(),
+		AddressIPVersion: IPv6,
+	}
+
+	response, err := client.CreateLoadBalancer(&creationArgs)
+	if err != nil {
+		t.Fatalf("Failed to CreateLoadBalancer: %v", err)
+	}
+
+	t.Logf("CreateLoadBalancer result: %v", *response)
+	lbId := response.LoadBalancerId
+
+	describeLoadBalancersArgs := DescribeLoadBalancersArgs{
+		RegionId: common.Hangzhou,
+	}
+
+	loadBalancers, err := client.DescribeLoadBalancers(&describeLoadBalancersArgs)
+
+	if err != nil {
+		t.Fatalf("Failed to DescribeLoadBalancers: %v", err)
+	}
+	t.Logf("DescribeLoadBalancers result: %++v", loadBalancers)
+
+	err = client.SetLoadBalancerStatus(lbId, InactiveStatus)
+	if err != nil {
+		t.Fatalf("Failed to SetLoadBalancerStatus: %v", err)
+	}
+	err = client.SetLoadBalancerName(lbId, "test-slb2")
+	if err != nil {
+		t.Fatalf("Failed to SetLoadBalancerName: %v", err)
+	}
+	loadBalancer, err := client.DescribeLoadBalancerAttribute(lbId)
+
+	if err != nil {
+		t.Fatalf("Failed to DescribeLoadBalancerAttribute: %v", err)
+	}
+	t.Logf("DescribeLoadBalancerAttribute result: %++v", loadBalancer)
+
+	err = client.DeleteLoadBalancer(lbId)
+	if err != nil {
+		t.Errorf("Failed to DeleteLoadBalancer: %v", err)
+	}
+
+	t.Logf("DeleteLoadBalancer successfully: %s", lbId)
+
+}
+
 func TestClient_DescribeLoadBalancers(t *testing.T) {
 	client := NewTestNewSLBClientForDebug()
 	//client.SetSecurityToken(TestSecurityToken)
@@ -79,3 +137,158 @@ func TestClient_DescribeLoadBalancers(t *testing.T) {
 		t.Logf("Result = %++v", slbs)
 	}
 }
+
+func TestClient_SetLoadBalancerDeleteProtection(t *testing.T) {
+	client := NewTestNewSLBClientForDebug()
+
+	creationArgs := CreateLoadBalancerArgs{
+		RegionId:         common.Beijing,
+		LoadBalancerName: "test-slb",
+		LoadBalancerSpec: S2Medium,
+		AddressType:      InternetAddressType,
+		ClientToken:      client.GenerateClientToken(),
+	}
+
+	response, err := client.CreateLoadBalancer(&creationArgs)
+	if err != nil {
+		t.Fatalf("Failed to CreateLoadBalancer: %v", err)
+	}
+
+	t.Logf("CreateLoadBalancer result: %v", *response)
+	lbId := response.LoadBalancerId
+
+	args := &SetLoadBalancerDeleteProtectionArgs{
+		LoadBalancerId:   lbId,
+		DeleteProtection: OnFlag,
+		RegionId:         common.Beijing,
+	}
+
+	err = client.SetLoadBalancerDeleteProtection(args)
+	if err != nil {
+		t.Fatalf("Failed %++v", err)
+	}
+	t.Logf("SetLoadBalancerDeleteProtection result: %v", *response)
+
+	err = client.DeleteLoadBalancer(lbId)
+	if err != nil {
+		t.Logf("DeleteLoadBalancer result: %++v", err)
+	} else {
+		t.Fatalf("Failed to set LoadBalancer delete protection.")
+	}
+}
+
+func TestClient_SetLoadBalancerModificationProtection(t *testing.T) {
+	client := NewTestNewSLBClientForDebug()
+
+	creationArgs := CreateLoadBalancerArgs{
+		RegionId:                     common.Beijing,
+		LoadBalancerName:             "test-slb-modification-protection",
+		LoadBalancerSpec:             S1Small,
+		AddressType:                  InternetAddressType,
+		ModificationProtectionStatus: ConsoleProtection,
+		ModificationProtectionReason: "kubernetes.do.not.delete",
+		ClientToken:                  client.GenerateClientToken(),
+	}
+	response, err := client.CreateLoadBalancer(&creationArgs)
+	if err != nil {
+		t.Fatalf("Failed to CreateLoadBalancer: %v", err)
+	}
+
+	t.Logf("CreateLoadBalancer result: %v", *response)
+	lbId := response.LoadBalancerId
+
+	lb, err := client.DescribeLoadBalancerAttribute(lbId)
+	if err != nil {
+		t.Fatalf("Failed to DescribeLoadBalancerAttribute: %v", err)
+	}
+
+	if lb.ModificationProtectionStatus != ConsoleProtection {
+		t.Fatalf("Failed to SetLoadBalancerModificationProtection, slb %s, expected %s got %s",
+			lbId, ConsoleProtection, lb.ModificationProtectionStatus)
+	}
+
+	args := SetLoadBalancerModificationProtectionArgs{
+		RegionId:                     common.Beijing,
+		LoadBalancerId:               lbId,
+		ModificationProtectionStatus: NonProtection,
+	}
+
+	err = client.SetLoadBalancerModificationProtection(&args)
+	if err != nil {
+		t.Fatalf("Failed to SetLoadBalancerModificationProtection: %v", err)
+	}
+
+	lb, err = client.DescribeLoadBalancerAttribute(lbId)
+	if err != nil {
+		t.Fatalf("Failed to DescribeLoadBalancerAttribute: %v", err)
+	}
+
+	if lb.ModificationProtectionStatus != NonProtection {
+		t.Fatalf("Failed to SetLoadBalancerModificationProtection, slb %s, expected %s got %s",
+			lbId, ConsoleProtection, lb.ModificationProtectionStatus)
+	}
+
+	// Delete Slb
+	err = client.DeleteLoadBalancer(lbId)
+	if err != nil {
+		t.Fatalf("Failed to DeleteLoadBalancer: %v", err)
+	}
+
+}
+
+func TestClient_ServiceManagedControl(t *testing.T) {
+	client := NewTestNewSLBClientForDebug()
+
+	creationArgs := CreateLoadBalancerArgs{
+		RegionId:                     common.Beijing,
+		LoadBalancerName:             "test-slb-modification-protection",
+		LoadBalancerSpec:             S1Small,
+		AddressType:                  InternetAddressType,
+		ModificationProtectionStatus: ConsoleProtection,
+		ModificationProtectionReason: "kubernetes.do.not.delete",
+		ClientToken:                  client.GenerateClientToken(),
+	}
+	response, err := client.CreateLoadBalancer(&creationArgs)
+	if err != nil {
+		t.Fatalf("Failed to CreateLoadBalancer: %v", err)
+	}
+
+	t.Logf("CreateLoadBalancer result: %v", *response)
+	lbId := response.LoadBalancerId
+
+	lb, err := client.DescribeLoadBalancerAttribute(lbId)
+	if err != nil {
+		t.Fatalf("Failed to DescribeLoadBalancerAttribute: %v", err)
+	}
+
+	resource := make([]ManagedResourceModel, 0)
+	resource = append(resource, ManagedResourceModel{ResourceId: lb.LoadBalancerId})
+	args := ServiceManagedControlArgs{
+		RegionId:           common.Beijing,
+		ServiceManagedMode: Managed,
+		ResourceType:       ManagedLoadBalancer,
+		Resources:          resource,
+	}
+
+	err = client.ServiceManagedControl(&args)
+	if err != nil {
+		t.Fatalf("Failed to modify resource managed status: %v", err)
+	}
+
+	lb, err = client.DescribeLoadBalancerAttribute(lbId)
+	if err != nil {
+		t.Fatalf("Failed to DescribeLoadBalancerAttribute: %v", err)
+	}
+
+	if lb.ServiceManagedMode != Managed {
+		t.Fatalf("Failed to modify resource managed status, slb %s, expected %s got %s",
+			lbId, Managed, lb.ServiceManagedMode)
+	}
+
+	// Delete Slb
+	err = client.DeleteLoadBalancer(lbId)
+	if err != nil {
+		t.Fatalf("Failed to DeleteLoadBalancer: %v", err)
+	}
+
+}
diff --git a/slb/rules_test.go b/slb/rules_test.go
index 79d59aa..2eb00b2 100644
--- a/slb/rules_test.go
+++ b/slb/rules_test.go
@@ -11,8 +11,8 @@ func TestCreateRules(t *testing.T) {
 	client := NewTestClientForDebug()
 
 	rulesArr := []Rule{
-		Rule{RuleName: "rule-001", Domain: "datapaking.com", Url: "/rule0001", VServerGroupId: TestVServerGroupID},
-		Rule{RuleName: "rule-002", Domain: "datapaking.com", Url: "/rule0002", VServerGroupId: TestVServerGroupID},
+		{RuleName: "rule-001", Domain: "datapaking.com", Url: "/rule0001", VServerGroupId: TestVServerGroupID},
+		{RuleName: "rule-002", Domain: "datapaking.com", Url: "/rule0002", VServerGroupId: TestVServerGroupID},
 	}
 	ruleStr, _ := json.Marshal(rulesArr)
 
diff --git a/slb/servers.go b/slb/servers.go
index 18be458..7e212dc 100644
--- a/slb/servers.go
+++ b/slb/servers.go
@@ -2,7 +2,6 @@ package slb
 
 import (
 	"encoding/json"
-
 	"github.com/denverdino/aliyungo/common"
 )
 
@@ -63,7 +62,7 @@ func (client *Client) AddBackendServers(loadBalancerId string, backendServers []
 
 type RemoveBackendServersArgs struct {
 	LoadBalancerId string
-	BackendServers []string
+	BackendServers string
 }
 
 type RemoveBackendServersResponse struct {
@@ -77,12 +76,15 @@ type RemoveBackendServersResponse struct {
 // RemoveBackendServers Remove backend servers
 //
 // You can read doc at http://docs.aliyun.com/#/pub/slb/api-reference/api-related-backendserver&RemoveBackendServers
-func (client *Client) RemoveBackendServers(loadBalancerId string, backendServers []string) (result []BackendServerType, err error) {
+func (client *Client) RemoveBackendServers(loadBalancerId string, backendServers []BackendServerType) (result []BackendServerType, err error) {
+	bytes, _ := json.Marshal(backendServers)
+
 	args := &RemoveBackendServersArgs{
 		LoadBalancerId: loadBalancerId,
-		BackendServers: backendServers,
+		BackendServers: string(bytes),
 	}
 	response := &RemoveBackendServersResponse{}
+
 	err = client.Invoke("RemoveBackendServers", args, response)
 	if err != nil {
 		return nil, err
diff --git a/slb/servers_test.go b/slb/servers_test.go
index 40d3aba..2b8f124 100644
--- a/slb/servers_test.go
+++ b/slb/servers_test.go
@@ -5,10 +5,16 @@ import "testing"
 func testBackendServers(t *testing.T, client *Client, loadBalancerId string) {
 
 	backendServers := []BackendServerType{
-		BackendServerType{
+		{
 			ServerId: TestInstanceId,
 			Weight:   100,
+			Type:     "ecs",
 		},
+		//BackendServerType{
+		//	ServerId: TestENIId,
+		//	Weight:   100,
+		//	Type:     "eni",
+		//},
 	}
 
 	servers, err := client.AddBackendServers(loadBalancerId, backendServers)
@@ -29,7 +35,7 @@ func testBackendServers(t *testing.T, client *Client, loadBalancerId string) {
 
 	t.Logf("Backend servers: %++v", servers)
 
-	servers, err = client.RemoveBackendServers(loadBalancerId, []string{TestInstanceId})
+	servers, err = client.RemoveBackendServers(loadBalancerId, backendServers)
 	if err != nil {
 		t.Errorf("Failed to RemoveBackendServers: %v", err)
 	}
diff --git a/slb/tags_test.go b/slb/tags_test.go
index 1b1a615..2ec4bc0 100644
--- a/slb/tags_test.go
+++ b/slb/tags_test.go
@@ -11,8 +11,8 @@ func TestAddTags(t *testing.T) {
 	client := NewTestClientForDebug()
 
 	tagItemArr := []TagItem{
-		TagItem{TagKey: "username", TagValue: "test"},
-		TagItem{TagKey: "birdthday", TagValue: "20170101"},
+		{TagKey: "username", TagValue: "test"},
+		{TagKey: "birdthday", TagValue: "20170101"},
 	}
 	tagItems, _ := json.Marshal(tagItemArr)
 
@@ -34,8 +34,8 @@ func TestRemoveTags(t *testing.T) {
 	client := NewTestClientForDebug()
 
 	tagItemArr := []TagItem{
-		TagItem{TagKey: "username", TagValue: "test"},
-		TagItem{TagKey: "birdthday", TagValue: "20170101"},
+		{TagKey: "username", TagValue: "test"},
+		{TagKey: "birdthday", TagValue: "20170101"},
 	}
 	tagItems, _ := json.Marshal(tagItemArr)
 
diff --git a/slb/vserver_group.go b/slb/vserver_group.go
index 6181b19..f621f28 100644
--- a/slb/vserver_group.go
+++ b/slb/vserver_group.go
@@ -5,13 +5,16 @@ import (
 )
 
 type VBackendServerType struct {
-	ServerId string
-	Weight   int
-	Port     int
-	Type     string
+	ServerId    string
+	Weight      int
+	Port        int
+	Type        string
+	ServerIp    string
+	Description string
 }
 
 type VServerGroup struct {
+	RegionId         common.Region
 	VServerGroupName string
 	VServerGroupId   string
 }
@@ -51,8 +54,10 @@ type DeleteVServerGroupArgs struct {
 }
 
 type DescribeVServerGroupsArgs struct {
-	LoadBalancerId string
-	RegionId       common.Region
+	LoadBalancerId  string
+	RegionId        common.Region
+	IncludeRule     bool
+	IncludeListener bool
 }
 
 type DescribeVServerGroupAttributeArgs struct {
@@ -83,8 +88,18 @@ type DescribeVServerGroupsResponse struct {
 	VServerGroups struct {
 		VServerGroup []VServerGroup
 	}
+	AssociatedObjects struct {
+		Listeners string
+		Rules     string
+	}
+}
+type DescribeVServerGroupAttributeResponse struct {
+	common.Response
+	VServerGroupId   string
+	VServerGroupName string
+	LoadBalancerId   string
+	BackendServers   VBackendServers
 }
-type DescribeVServerGroupAttributeResponse CreateVServerGroupResponse
 
 func (client *Client) CreateVServerGroup(args *CreateVServerGroupArgs) (response *CreateVServerGroupResponse, err error) {
 	response = &CreateVServerGroupResponse{}
diff --git a/slb/vserver_group_test.go b/slb/vserver_group_test.go
index fa57a00..becd07a 100644
--- a/slb/vserver_group_test.go
+++ b/slb/vserver_group_test.go
@@ -4,6 +4,8 @@ import (
 	"encoding/json"
 	"testing"
 
+	"fmt"
+
 	"github.com/denverdino/aliyungo/common"
 )
 
@@ -57,17 +59,16 @@ func TestCreateVServerGroup(t *testing.T) {
 
 func TestDescribeVServerGroups(t *testing.T) {
 	arg := &DescribeVServerGroupsArgs{
-		LoadBalancerId: loadBalancerId,
-		RegionId:       region,
+		LoadBalancerId:  loadBalancerId,
+		RegionId:        region,
+		IncludeListener: true,
+		IncludeRule:     true,
 	}
 	response, err := client.DescribeVServerGroups(arg)
 	if err != nil {
 		t.Error(err)
 	} else {
-		t.Log(response)
-		for _, vserverGroup := range response.VServerGroups.VServerGroup {
-			deleteVServerGroupIdList = append(deleteVServerGroupIdList, vserverGroup.VServerGroupId)
-		}
+		fmt.Println(PrettyJson(response))
 	}
 }
 
diff --git a/slb/zones.go b/slb/zones.go
new file mode 100644
index 0000000..f9180b2
--- /dev/null
+++ b/slb/zones.go
@@ -0,0 +1,51 @@
+package slb
+
+import (
+	"github.com/denverdino/aliyungo/common"
+)
+
+type DescribeZonesArgs struct {
+	RegionId common.Region
+}
+
+//
+// You can read doc at http://docs.aliyun.com/#/pub/ecs/open-api/datatype&zonetype
+type ZoneType struct {
+	ZoneId     string
+	LocalName  string
+	SlaveZones struct {
+		SlaveZone []ZoneType
+	}
+}
+
+type DescribeZonesResponse struct {
+	common.Response
+	Zones struct {
+		Zone []ZoneType
+	}
+}
+
+// DescribeZones describes zones
+func (client *Client) DescribeZones(regionId common.Region) (zones []ZoneType, err error) {
+	response, err := client.DescribeZonesWithRaw(regionId)
+	if err == nil {
+		return response.Zones.Zone, nil
+	}
+
+	return []ZoneType{}, err
+}
+
+func (client *Client) DescribeZonesWithRaw(regionId common.Region) (response *DescribeZonesResponse, err error) {
+	args := DescribeZonesArgs{
+		RegionId: regionId,
+	}
+	response = &DescribeZonesResponse{}
+
+	err = client.Invoke("DescribeZones", &args, response)
+
+	if err == nil {
+		return response, nil
+	}
+
+	return nil, err
+}
diff --git a/slb/zones_test.go b/slb/zones_test.go
new file mode 100644
index 0000000..11cb4e0
--- /dev/null
+++ b/slb/zones_test.go
@@ -0,0 +1,20 @@
+package slb
+
+import (
+	"github.com/denverdino/aliyungo/common"
+	"testing"
+)
+
+func TestDescribeZones(t *testing.T) {
+
+	client := NewTestNewSLBClientForDebug()
+
+	zones, err := client.DescribeZones(common.Hangzhou)
+
+	if err == nil {
+		t.Logf("regions: %v", zones)
+	} else {
+		t.Errorf("Failed to DescribeZones: %v", err)
+	}
+
+}
diff --git a/sls/client.go b/sls/client.go
index e3e6b18..bac2913 100644
--- a/sls/client.go
+++ b/sls/client.go
@@ -28,6 +28,14 @@ func (client *Client) SetDebug(debug bool) {
 	client.debug = debug
 }
 
+// SetTransport sets transport to the http client
+func (client *Client) SetTransport(transport http.RoundTripper) {
+	if client.httpClient == nil {
+		client.httpClient = &http.Client{}
+	}
+	client.httpClient.Transport = transport
+}
+
 type Project struct {
 	client      *Client
 	Name        string `json:"projectName,omitempty"`
diff --git a/sls/index.go b/sls/index.go
index 8d06d9b..86389b0 100644
--- a/sls/index.go
+++ b/sls/index.go
@@ -1,22 +1,28 @@
 package sls
 
-import "encoding/json"
+import (
+	"encoding/json"
+)
 
 type IndexLineConfig struct {
 	TokenList     []string `json:"token,omitempty"`
 	CaseSensitive bool     `json:"caseSensitive"`
 	IncludeKeys   []string `json:"include_keys,omitempty"`
-	Exclude_keys  []string `json:"exclude_keys,omitempty"`
+	ExcludeKeys   []string `json:"exclude_keys,omitempty"`
 }
 
 type IndexKeyConfig struct {
+	Type          string   `json:"type,omitempty"`
+	Alias         string   `json:"alias,omitempty"`
+	Chn           bool     `json:"chn"`
 	TokenList     []string `json:"token,omitempty"`
-	CaseSensitive bool     `json:"caseSensitive,omitempty"`
+	CaseSensitive bool     `json:"caseSensitive"`
+	DocValue      bool     `json:"doc_value"`
 }
 
 type IndexConfig struct {
 	TTL           int                       `json:"ttl,omitempty"`
-	LineConfig    IndexLineConfig           `json:"line,omitempty"`
+	LineConfig    *IndexLineConfig          `json:"line,omitempty"`
 	KeyConfigList map[string]IndexKeyConfig `json:"keys,omitempty"`
 }
 
diff --git a/sls/index_test.go b/sls/index_test.go
index a66c268..3a08047 100644
--- a/sls/index_test.go
+++ b/sls/index_test.go
@@ -10,7 +10,7 @@ func TestCreateIndex(t *testing.T) {
 	p := DefaultProject(t)
 	config := &IndexConfig{
 		TTL: 7,
-		LineConfig: IndexLineConfig{
+		LineConfig: &IndexLineConfig{
 			TokenList:     []string{",", "\t", "\n", " ", ";"},
 			CaseSensitive: false,
 		},
diff --git a/sls/machine_group.go b/sls/machine_group.go
index 62efa2d..4128041 100644
--- a/sls/machine_group.go
+++ b/sls/machine_group.go
@@ -2,7 +2,6 @@ package sls
 
 import (
 	"encoding/json"
-	"errors"
 	"fmt"
 	"strconv"
 )
@@ -154,5 +153,5 @@ func (proj *Project) GetAppliedConfigs(machineGroup string) ([]string, error) {
 		return v, nil
 	}
 
-	return nil, errors.New(fmt.Sprintf("%v is not a string array", configs["config"]))
+	return nil, fmt.Errorf("%v is not a string array", configs["config"])
 }
diff --git a/sls/request.go b/sls/request.go
index 8688a98..4a54ebc 100644
--- a/sls/request.go
+++ b/sls/request.go
@@ -8,6 +8,7 @@ import (
 	"io/ioutil"
 	"log"
 	"net/http"
+	"net/http/httputil"
 	"net/url"
 	"strconv"
 	"time"
@@ -91,14 +92,19 @@ func (client *Client) doRequest(req *request) (*http.Response, error) {
 	if err != nil {
 		return nil, err
 	}
+	if client.debug {
+		reqDump, _ := httputil.DumpRequest(hreq, true)
+		log.Printf("---------------REQUEST---------------\n%s\n\n", string(reqDump))
+	}
 	t0 := time.Now()
 	resp, err := client.httpClient.Do(hreq)
 	t1 := time.Now()
 	if err != nil {
 		return nil, err
 	}
-
 	if client.debug {
+		resDump, _ := httputil.DumpResponse(resp, true)
+		log.Printf("---------------RESPONSE---------------\n%s\n\n", string(resDump))
 		log.Printf("Invoke %s %s %d (%v)", req.method, req.url(), resp.StatusCode, t1.Sub(t0))
 	}
 
diff --git a/sls/signature.go b/sls/signature.go
index 180bb31..f962c28 100644
--- a/sls/signature.go
+++ b/sls/signature.go
@@ -51,7 +51,7 @@ func canonicalizeResource(req *request) string {
 	canonicalizedResource := req.path
 	var paramNames []string
 	if req.params != nil && len(req.params) > 0 {
-		for k, _ := range req.params {
+		for k := range req.params {
 			paramNames = append(paramNames, k)
 		}
 		sort.Strings(paramNames)
@@ -69,7 +69,7 @@ func canonicalizeResource(req *request) string {
 func canonicalizeHeader(headers map[string]string) string {
 	var canonicalizedHeaders []string
 
-	for k, _ := range headers {
+	for k := range headers {
 		if lower := strings.ToLower(k); strings.HasPrefix(lower, HeaderSLSPrefix1) || strings.HasPrefix(lower, HeaderSLSPrefix2) {
 			canonicalizedHeaders = append(canonicalizedHeaders, lower)
 		}
diff --git a/sts/assume_role_test.go b/sts/assume_role_test.go
index 1b1869f..79fba6e 100644
--- a/sts/assume_role_test.go
+++ b/sts/assume_role_test.go
@@ -26,7 +26,7 @@ var (
 
 	princpalPolicyDocument = ram.AssumeRolePolicyDocument{
 		Statement: []ram.AssumeRolePolicyItem{
-			ram.AssumeRolePolicyItem{Action: "sts:AssumeRole", Effect: "Allow", Principal: princpal},
+			{Action: "sts:AssumeRole", Effect: "Allow", Principal: princpal},
 		},
 		Version: "1"}
 
@@ -39,7 +39,7 @@ var (
 
 var policyDocument = ram.PolicyDocument{
 	Statement: []ram.PolicyItem{
-		ram.PolicyItem{
+		{
 			Action:   "oss:GetObject",
 			Effect:   "Allow",
 			Resource: "acs:oss:*:*:*/anyprefix",
@@ -66,7 +66,7 @@ func createAssumeRoleRequest(roleArn string) AssumeRoleRequest {
 func createPolicyDocument() *ram.PolicyDocument {
 	return &ram.PolicyDocument{
 		Statement: []ram.PolicyItem{
-			ram.PolicyItem{
+			{
 				Action:   "oss:GetObject",
 				Effect:   "Allow",
 				Resource: "acs:oss:*:*:*/*",
diff --git a/util/attempt_test.go b/util/attempt_test.go
index 50e9be7..31cfba0 100644
--- a/util/attempt_test.go
+++ b/util/attempt_test.go
@@ -14,9 +14,9 @@ func TestAttemptTiming(t *testing.T) {
 	got := make([]time.Duration, 0, len(want)) // avoid allocation when testing timing
 	t0 := time.Now()
 	for a := testAttempt.Start(); a.Next(); {
-		got = append(got, time.Now().Sub(t0))
+		got = append(got, time.Since(t0))
 	}
-	got = append(got, time.Now().Sub(t0))
+	got = append(got, time.Since(t0))
 	if len(got) != len(want) {
 		t.Fatalf("Failed!")
 	}
diff --git a/util/encoding.go b/util/encoding.go
index ec1a5b1..ec06e66 100644
--- a/util/encoding.go
+++ b/util/encoding.go
@@ -47,7 +47,7 @@ func setQueryValues(i interface{}, values *url.Values, prefix string) {
 	// add to support url.Values
 	mapValues, ok := i.(url.Values)
 	if ok {
-		for k, _ := range mapValues {
+		for k := range mapValues {
 			values.Set(k, mapValues.Get(k))
 		}
 		return
@@ -191,7 +191,7 @@ func setQueryValuesByFlattenMethod(i interface{}, values *url.Values, prefix str
 	// add to support url.Values
 	mapValues, ok := i.(url.Values)
 	if ok {
-		for k, _ := range mapValues {
+		for k := range mapValues {
 			values.Set(k, mapValues.Get(k))
 		}
 		return
diff --git a/util/encoding_test.go b/util/encoding_test.go
index 0d93ce3..537bf5f 100644
--- a/util/encoding_test.go
+++ b/util/encoding_test.go
@@ -65,8 +65,8 @@ func TestConvertToQueryValues(t *testing.T) {
 		StringArray:  []string{"abc", "xyz"},
 		StringArray2: []string{"abc", "xyz"},
 		StructArray: []SubStruct{
-			SubStruct{A: "a", B: 1},
-			SubStruct{A: "x", B: 2},
+			{A: "a", B: 1},
+			{A: "x", B: 2},
 		},
 		SubStruct: SubStruct{A: "M", B: 0},
 		test:      TestString("test"),
diff --git a/util/util.go b/util/util.go
index dd68214..15a990d 100644
--- a/util/util.go
+++ b/util/util.go
@@ -4,6 +4,8 @@ import (
 	"bytes"
 	srand "crypto/rand"
 	"encoding/binary"
+	"encoding/json"
+	"fmt"
 	"math/rand"
 	"net/http"
 	"net/url"
@@ -64,6 +66,34 @@ func Encode(v url.Values) string {
 	return buf.String()
 }
 
+// Like Encode, but key and value are not escaped
+func EncodeWithoutEscape(v url.Values) string {
+	if v == nil {
+		return ""
+	}
+	var buf bytes.Buffer
+	keys := make([]string, 0, len(v))
+	for k := range v {
+		keys = append(keys, k)
+	}
+	sort.Strings(keys)
+	for _, k := range keys {
+		vs := v[k]
+		prefix := k
+		for _, v := range vs {
+			if buf.Len() > 0 {
+				buf.WriteByte('&')
+			}
+			buf.WriteString(prefix)
+			if v != "" {
+				buf.WriteString("=")
+				buf.WriteString(v)
+			}
+		}
+	}
+	return buf.String()
+}
+
 func GetGMTime() string {
 	return time.Now().UTC().Format(http.TimeFormat)
 }
@@ -145,3 +175,11 @@ func GenerateRandomECSPassword() string {
 	return string(s)
 
 }
+
+func PrettyJson(object interface{}) string {
+	b, err := json.MarshalIndent(object, "", "    ")
+	if err != nil {
+		fmt.Printf("ERROR: PrettyJson, %v\n %s\n", err, b)
+	}
+	return string(b)
+}

Debdiff

[The following lists of changes regard files as different if they have different names, permissions or owners.]

Files in second set of .debs but not in first

-rw-r--r--  root/root   /usr/share/gocode/src/github.com/denverdino/aliyungo/cdn/auth/random_uuid.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/denverdino/aliyungo/cdn/auth/random_uuid_test.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/denverdino/aliyungo/cdn/auth/sign_url.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/denverdino/aliyungo/cdn/auth/sign_url_test.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/denverdino/aliyungo/cen/client.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/denverdino/aliyungo/cen/route.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/denverdino/aliyungo/cen/route_test.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/denverdino/aliyungo/common/logger.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/denverdino/aliyungo/common/logger_test.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/denverdino/aliyungo/common/utils/utils.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/denverdino/aliyungo/cs/kubernetes_cluster_test.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/denverdino/aliyungo/cs/kubernets_cluster.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/denverdino/aliyungo/cs/node_pool.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/denverdino/aliyungo/cs/node_pool_test.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/denverdino/aliyungo/cs/serverless.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/denverdino/aliyungo/cs/serverless_test.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/denverdino/aliyungo/cs/token.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/denverdino/aliyungo/cs/token_test.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/denverdino/aliyungo/ecs/eni_test.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/denverdino/aliyungo/ecs/route_entry.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/denverdino/aliyungo/ecs/route_entry_test.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/denverdino/aliyungo/go.mod
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/denverdino/aliyungo/go.sum
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/denverdino/aliyungo/ros/rpc_client.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/denverdino/aliyungo/ros/standard/client.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/denverdino/aliyungo/ros/standard/client_test.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/denverdino/aliyungo/slb/zones.go
-rw-r--r--  root/root   /usr/share/gocode/src/github.com/denverdino/aliyungo/slb/zones_test.go
-rwxr-xr-x  root/root   /usr/share/gocode/src/github.com/denverdino/aliyungo/ros/standard/stack.go

No differences were encountered in the control files

More details

Full run details