New Upstream Snapshot - golang-github-denverdino-aliyungo
Ready changes
Summary
Merged new upstream version: 0.0~git20220929.e3c8bf5 (was: 0.0~git20180921.13fa8aa).
Resulting package
Built on 2022-11-03T09:13 (took 5m37s)
The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:
apt install -t fresh-snapshots golang-github-denverdino-aliyungo-dev
Lintian Result
- golang-github-denverdino-aliyungo-dev_0.0~git20220929.e3c8bf5-1~jan+nus1_all.deb
- golang-github-denverdino-aliyungo_0.0~git20220929.e3c8bf5-1~jan+nus1.dsc
- golang-github-denverdino-aliyungo_0.0~git20220929.e3c8bf5-1~jan+nus1_amd64.buildinfo
- golang-github-denverdino-aliyungo_0.0~git20220929.e3c8bf5-1~jan+nus1_amd64.changes
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/.gitignore b/.gitignore
deleted file mode 100644
index 485dee6..0000000
--- a/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-.idea
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..cf0b99d
--- /dev/null
+++ b/cs/node_pool.go
@@ -0,0 +1,258 @@
+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"`
+
+ PolarDBIds []string `json:"polardb_ids"`
+}
+
+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..6666e83 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+golang-github-denverdino-aliyungo (0.0~git20220929.e3c8bf5-1) UNRELEASED; urgency=low
+
+ * New upstream snapshot.
+
+ -- Debian Janitor <janitor@jelmer.uk> Thu, 03 Nov 2022 09:08:55 -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