Compare commits

...

7 Commits

Author SHA1 Message Date
1972956aeb v1.6.0-gamma 2023-09-15 12:41:04 +03:00
afcbc7e749 1.6.0-beta 2023-09-14 15:05:38 +03:00
0b3de4df7f 1.6.0-alfa 2023-09-11 13:11:33 +03:00
c0608d08b9 1.5.8-gostech 2023-09-07 18:11:25 +03:00
4d9b8fc9d8 v1.5.7 2023-09-04 18:48:22 +03:00
e8270453cc 1.5.6 2023-08-30 13:54:30 +03:00
7de095302b v1.5.5 2023-08-25 13:53:43 +03:00
18 changed files with 335 additions and 299 deletions

4
.gitignore vendored
View File

@@ -1,3 +1,5 @@
cmd/
.idea/
.vscode/
.vscode/
.fleet/
.DS_Store

View File

@@ -1,9 +1,4 @@
## Version 1.5.4
### Feature
- Added cloudbroker/stack group of endpoints support. Added the possibility to get information about stacks (list handler) and about a specific stack by ID (get handler)
- Added cloudbroker/flipgroup group of endpoints support. Added the possibility to get information about flipgroup (list handler) and about a specific flipgroup by ID (get handler), add and remote compute, create, edit and delete flipgroup.
## Version 1.6.0-beta
### Bugfix
- Fixed the field image/models/history.guid, added a custom unmarshall. Fixed an error with get information about images (handlers - list, get) in cloudbroker
- Refactored client, made it concurrent safe

View File

@@ -1,24 +1,31 @@
package decortsdk
import (
"bytes"
"context"
"crypto/tls"
"errors"
"fmt"
"io"
"net/http"
"strings"
"sync"
"time"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker"
"github.com/google/go-querystring/query"
"repository.basistech.ru/BASIS/decort-golang-sdk/config"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/client"
)
// HTTP-client for platform
type DecortClient struct {
decortURL string
client *http.Client
decortURL string
client *http.Client
cfg config.Config
expiryTime time.Time
mutex *sync.Mutex
}
// Сlient builder
@@ -27,9 +34,25 @@ func New(cfg config.Config) *DecortClient {
cfg.Retries = 5
}
var expiryTime time.Time
if cfg.Token != "" {
expiryTime = time.Now().AddDate(0, 0, 1)
}
return &DecortClient{
decortURL: cfg.DecortURL,
client: client.NewHttpClient(cfg),
client: &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
//nolint:gosec
InsecureSkipVerify: cfg.SSLSkipVerify,
},
},
},
cfg: cfg,
expiryTime: expiryTime,
mutex: &sync.Mutex{},
}
}
@@ -56,7 +79,11 @@ func (dc *DecortClient) DecortApiCall(ctx context.Context, method, url string, p
return nil, err
}
resp, err := dc.client.Do(req)
if err = dc.getToken(ctx); err != nil {
return nil, err
}
resp, err := dc.do(req)
if err != nil {
return nil, err
}
@@ -73,3 +100,64 @@ func (dc *DecortClient) DecortApiCall(ctx context.Context, method, url string, p
return respBytes, nil
}
func (dc *DecortClient) getToken(ctx context.Context) error {
dc.mutex.Lock()
defer dc.mutex.Unlock()
if dc.cfg.Token == "" || time.Now().After(dc.expiryTime) {
body := fmt.Sprintf("grant_type=client_credentials&client_id=%s&client_secret=%s&response_type=id_token", dc.cfg.AppID, dc.cfg.AppSecret)
bodyReader := strings.NewReader(body)
dc.cfg.SSOURL = strings.TrimSuffix(dc.cfg.SSOURL, "/")
req, _ := http.NewRequestWithContext(ctx, "POST", dc.cfg.SSOURL+"/v1/oauth/access_token", bodyReader)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
resp, err := dc.client.Do(req)
if err != nil {
return fmt.Errorf("cannot get token: %w", err)
}
tokenBytes, _ := io.ReadAll(resp.Body)
resp.Body.Close()
if resp.StatusCode != 200 {
return fmt.Errorf("cannot get token: %s", tokenBytes)
}
token := string(tokenBytes)
dc.cfg.Token = token
dc.expiryTime = time.Now().AddDate(0, 0, 1)
}
return nil
}
func (dc *DecortClient) do(req *http.Request) (*http.Response, error) {
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Authorization", "bearer "+dc.cfg.Token)
req.Header.Set("Accept", "application/json")
var resp *http.Response
var err error
buf, _ := io.ReadAll(req.Body)
for i := uint64(0); i < dc.cfg.Retries; i++ {
req := req.Clone(req.Context())
req.Body = io.NopCloser(bytes.NewBuffer(buf))
resp, err = dc.client.Do(req)
if err == nil {
if resp.StatusCode == 200 {
return resp, err
}
respBytes, _ := io.ReadAll(resp.Body)
err = fmt.Errorf("%s", respBytes)
resp.Body.Close()
}
}
return nil, fmt.Errorf("could not execute request: %w", err)
}

View File

@@ -1,40 +0,0 @@
package client
import (
"crypto/tls"
"net/http"
"time"
"repository.basistech.ru/BASIS/decort-golang-sdk/config"
)
func NewHttpClient(cfg config.Config) *http.Client {
transCfg := &http.Transport{
TLSClientConfig: &tls.Config{
//nolint:gosec
InsecureSkipVerify: cfg.SSLSkipVerify,
},
}
var expiredTime time.Time
if cfg.Token != "" {
expiredTime = time.Now().AddDate(0, 0, 1)
}
return &http.Client{
Transport: &transport{
base: transCfg,
retries: cfg.Retries,
clientID: cfg.AppID,
clientSecret: cfg.AppSecret,
ssoURL: cfg.SSOURL,
token: cfg.Token,
expiryTime: expiredTime,
//TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
Timeout: cfg.Timeout.Get(),
}
}

View File

@@ -1,40 +0,0 @@
package client
import (
"crypto/tls"
"net/http"
"net/url"
"time"
"repository.basistech.ru/BASIS/decort-golang-sdk/config"
)
// NewLegacyHttpClient creates legacy HTTP Client
func NewLegacyHttpClient(cfg config.LegacyConfig) *http.Client {
transCfg := &http.Transport{
TLSClientConfig: &tls.Config{
//nolint:gosec
InsecureSkipVerify: cfg.SSLSkipVerify,
},
}
var expiredTime time.Time
if cfg.Token != "" {
expiredTime = time.Now().AddDate(0, 0, 1)
}
return &http.Client{
Transport: &transportLegacy{
base: transCfg,
username: url.QueryEscape(cfg.Username),
password: url.QueryEscape(cfg.Password),
retries: cfg.Retries,
token: cfg.Token,
decortURL: cfg.DecortURL,
expiryTime: expiredTime,
},
Timeout: cfg.Timeout.Get(),
}
}

View File

@@ -1,74 +0,0 @@
package client
import (
"fmt"
"io"
"net/http"
"strings"
"time"
)
type transportLegacy struct {
base http.RoundTripper
username string
password string
retries uint64
token string
decortURL string
expiryTime time.Time
}
func (t *transportLegacy) RoundTrip(request *http.Request) (*http.Response, error) {
if t.token == "" || time.Now().After(t.expiryTime) {
body := fmt.Sprintf("username=%s&password=%s", t.username, t.password)
bodyReader := strings.NewReader(body)
req, _ := http.NewRequestWithContext(request.Context(), "POST", t.decortURL+"/restmachine/cloudapi/user/authenticate", bodyReader)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
resp, err := t.base.RoundTrip(req)
if err != nil {
return nil, fmt.Errorf("unable to get token: %w", err)
}
tokenBytes, _ := io.ReadAll(resp.Body)
resp.Body.Close()
if resp.StatusCode != 200 {
return nil, fmt.Errorf("unable to get token: %s", tokenBytes)
}
token := string(tokenBytes)
t.token = token
t.expiryTime = time.Now().AddDate(0, 0, 1)
}
tokenValue := fmt.Sprintf("&authkey=%s", t.token)
tokenReader := strings.NewReader(tokenValue)
newBody := io.MultiReader(request.Body, tokenReader)
req, _ := http.NewRequestWithContext(request.Context(), request.Method, request.URL.String(), newBody)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Accept", "application/json")
var resp *http.Response
var err error
for i := uint64(0); i < t.retries; i++ {
resp, err = t.base.RoundTrip(req)
if err == nil {
if resp.StatusCode == 200 {
return resp, nil
}
respBytes, _ := io.ReadAll(resp.Body)
err = fmt.Errorf("%s", respBytes)
resp.Body.Close()
}
if err != nil {
return nil, fmt.Errorf("could not execute request: %w", err)
}
time.Sleep(time.Second * 5)
}
return nil, fmt.Errorf("could not execute request: %w", err)
}

View File

@@ -1,69 +0,0 @@
package client
import (
"fmt"
"io"
"net/http"
"strings"
"time"
)
type transport struct {
base http.RoundTripper
retries uint64
clientID string
clientSecret string
token string
ssoURL string
expiryTime time.Time
}
func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) {
if t.token == "" || time.Now().After(t.expiryTime) {
body := fmt.Sprintf("grant_type=client_credentials&client_id=%s&client_secret=%s&response_type=id_token", t.clientID, t.clientSecret)
bodyReader := strings.NewReader(body)
t.ssoURL = strings.TrimSuffix(t.ssoURL, "/")
req, _ := http.NewRequestWithContext(req.Context(), "POST", t.ssoURL+"/v1/oauth/access_token", bodyReader)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
resp, err := t.base.RoundTrip(req)
if err != nil {
return nil, fmt.Errorf("cannot get token: %w", err)
}
tokenBytes, _ := io.ReadAll(resp.Body)
resp.Body.Close()
if resp.StatusCode != 200 {
return nil, fmt.Errorf("cannot get token: %s", tokenBytes)
}
token := string(tokenBytes)
t.token = token
t.expiryTime = time.Now().AddDate(0, 0, 1)
}
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Authorization", "bearer "+t.token)
req.Header.Set("Accept", "application/json")
var resp *http.Response
var err error
for i := uint64(0); i < t.retries; i++ {
resp, err = t.base.RoundTrip(req)
if err == nil {
if resp.StatusCode == 200 {
return resp, nil
}
respBytes, _ := io.ReadAll(resp.Body)
err = fmt.Errorf("%s", respBytes)
resp.Body.Close()
}
//logrus.Errorf("Could not execute request: %v. Retrying %d/%d", err, i+1, t.retries)
time.Sleep(time.Second * 5)
}
return nil, fmt.Errorf("could not execute request: %w", err)
}

View File

@@ -1,23 +1,31 @@
package decortsdk
import (
"bytes"
"context"
"crypto/tls"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"sync"
"time"
"github.com/google/go-querystring/query"
"repository.basistech.ru/BASIS/decort-golang-sdk/config"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/client"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker"
)
// Legacy HTTP-client for platform
type LegacyDecortClient struct {
decortURL string
client *http.Client
decortURL string
client *http.Client
cfg config.LegacyConfig
expiryTime time.Time
mutex *sync.Mutex
}
// Legacy client builder
@@ -26,9 +34,25 @@ func NewLegacy(cfg config.LegacyConfig) *LegacyDecortClient {
cfg.Retries = 5
}
var expiryTime time.Time
if cfg.Token != "" {
expiryTime = time.Now().AddDate(0, 0, 1)
}
return &LegacyDecortClient{
decortURL: cfg.DecortURL,
client: client.NewLegacyHttpClient(cfg),
client: &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
//nolint:gosec
InsecureSkipVerify: cfg.SSLSkipVerify,
},
},
},
cfg: cfg,
expiryTime: expiryTime,
mutex: &sync.Mutex{},
}
}
@@ -49,13 +73,18 @@ func (ldc *LegacyDecortClient) DecortApiCall(ctx context.Context, method, url st
return nil, err
}
body := strings.NewReader(values.Encode())
if err = ldc.getToken(ctx); err != nil {
return nil, err
}
body := strings.NewReader(values.Encode() + fmt.Sprintf("&authkey=%s", ldc.cfg.Token))
req, err := http.NewRequestWithContext(ctx, method, ldc.decortURL+"/restmachine"+url, body)
if err != nil {
return nil, err
}
resp, err := ldc.client.Do(req)
resp, err := ldc.do(req)
if err != nil {
return nil, err
}
@@ -72,3 +101,60 @@ func (ldc *LegacyDecortClient) DecortApiCall(ctx context.Context, method, url st
return respBytes, nil
}
func (ldc *LegacyDecortClient) getToken(ctx context.Context) error {
ldc.mutex.Lock()
defer ldc.mutex.Unlock()
if ldc.cfg.Token == "" || time.Now().After(ldc.expiryTime) {
body := fmt.Sprintf("username=%s&password=%s", url.QueryEscape(ldc.cfg.Username), url.QueryEscape(ldc.cfg.Password))
bodyReader := strings.NewReader(body)
req, _ := http.NewRequestWithContext(ctx, "POST", ldc.cfg.DecortURL+"/restmachine/cloudapi/user/authenticate", bodyReader)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
resp, err := ldc.client.Do(req)
if err != nil {
return fmt.Errorf("unable to get token: %w", err)
}
tokenBytes, _ := io.ReadAll(resp.Body)
resp.Body.Close()
if resp.StatusCode != 200 {
return fmt.Errorf("unable to get token: %s", tokenBytes)
}
token := string(tokenBytes)
ldc.cfg.Token = token
ldc.expiryTime = time.Now().AddDate(0, 0, 1)
}
return nil
}
func (ldc *LegacyDecortClient) do(req *http.Request) (*http.Response, error) {
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Accept", "application/json")
var resp *http.Response
var err error
buf, _ := io.ReadAll(req.Body)
for i := uint64(0); i < ldc.cfg.Retries; i++ {
req := req.Clone(req.Context())
req.Body = io.NopCloser(bytes.NewBuffer(buf))
resp, err = ldc.client.Do(req)
if err == nil {
if resp.StatusCode == 200 {
return resp, err
}
respBytes, _ := io.ReadAll(resp.Body)
err = fmt.Errorf("%s", respBytes)
resp.Body.Close()
}
}
return nil, fmt.Errorf("could not execute request: %w", err)
}

View File

@@ -106,6 +106,10 @@ type CreateRequest struct {
// Text description of this Kubernetes cluster
// Required: false
Description string `url:"desc,omitempty" json:"desc,omitempty"`
//Use only selected ExtNet for infrastructure connections
// Required: false
ExtNetOnly bool `url:"extnetOnly,omitempty" json:"extnetOnly,omitempty"`
}
// Create creates a new Kubernetes cluster in the specified Resource Group

View File

@@ -2,6 +2,7 @@ package lb
import (
"context"
"errors"
"net/http"
"strings"
@@ -20,16 +21,16 @@ type CreateRequest struct {
Name string `url:"name" json:"name" validate:"required"`
// External network to connect this load balancer to
// Required: true
ExtNetID uint64 `url:"extnetId" json:"extnetId" validate:"required"`
// Required: false
ExtNetID uint64 `url:"extnetId" json:"extnetId"`
// Internal network (VINS) to connect this load balancer to
// Required: true
VINSID uint64 `url:"vinsId" json:"vinsId" validate:"required"`
// Required: false
VINSID uint64 `url:"vinsId" json:"vinsId"`
// Start now Load balancer
// Required: true
Start bool `url:"start" json:"start" validate:"required"`
// Required: false
Start bool `url:"start" json:"start"`
// Text description of this load balancer
// Required: false
@@ -45,6 +46,10 @@ func (l LB) Create(ctx context.Context, req CreateRequest) (string, error) {
}
}
if req.ExtNetID == 0 && req.VINSID == 0 {
return "", errors.New ("vinsId and extNetId cannot be both in the value 0")
}
url := "/cloudapi/lb/create"
res, err := l.client.DecortApiCall(ctx, http.MethodPost, url, req)

View File

@@ -2,7 +2,7 @@ package grid
// FilterByID returns ListGrids with specified ID.
func (lg ListGrids) FilterByID(id uint64) ListGrids {
predicate := func(rg RecordGrid) bool {
predicate := func(rg ItemGridList) bool {
return rg.ID == id
}
@@ -11,7 +11,7 @@ func (lg ListGrids) FilterByID(id uint64) ListGrids {
// FilterByName returns ListGrids with specified Name.
func (lg ListGrids) FilterByName(name string) ListGrids {
predicate := func(rg RecordGrid) bool {
predicate := func(rg ItemGridList) bool {
return rg.Name == name
}
@@ -20,7 +20,7 @@ func (lg ListGrids) FilterByName(name string) ListGrids {
// FilterByLocationCode returns ListGrids with specified LocationCode.
func (lg ListGrids) FilterByLocationCode(locationCode string) ListGrids {
predicate := func(rg RecordGrid) bool {
predicate := func(rg ItemGridList) bool {
return rg.LocationCode == locationCode
}
@@ -28,7 +28,7 @@ func (lg ListGrids) FilterByLocationCode(locationCode string) ListGrids {
}
// FilterFunc allows filtering ListGrids based on a user-specified predicate.
func (lg ListGrids) FilterFunc(predicate func(RecordGrid) bool) ListGrids {
func (lg ListGrids) FilterFunc(predicate func(ItemGridList) bool) ListGrids {
var result ListGrids
for _, item := range lg.Data {
@@ -44,9 +44,9 @@ func (lg ListGrids) FilterFunc(predicate func(RecordGrid) bool) ListGrids {
// FindOne returns first found RecordGrid.
// If none was found, returns an empty struct.
func (lg ListGrids) FindOne() RecordGrid {
func (lg ListGrids) FindOne() ItemGridList {
if len(lg.Data) == 0 {
return RecordGrid{}
return ItemGridList{}
}
return lg.Data[0]

View File

@@ -3,7 +3,7 @@ package grid
import "testing"
var grids = ListGrids{
Data: []RecordGrid{
Data: []ItemGridList{
{
Resources: Resources{
Current: RecordResource{
@@ -123,7 +123,7 @@ func TestFilterByLocationCode(t *testing.T) {
}
func TestFilterFunc(t *testing.T) {
actual := grids.FilterFunc(func(rg RecordGrid) bool {
actual := grids.FilterFunc(func(rg ItemGridList) bool {
return rg.GID == 777
}).
FindOne()

View File

@@ -18,7 +18,7 @@ type RecordResourcesConsumption struct {
Reserved RecordResource `json:"Reserved"`
// GID
GID uint64 `json:"gid"`
GID uint64 `json:"id"`
}
type ListResourceConsumption struct {
@@ -67,6 +67,27 @@ type DiskUsage struct {
// Detailed information about grid
type RecordGrid struct {
// Flag
Flag string `json:"flag"`
// Grid ID
GID uint64 `json:"gid"`
// GUID
GUID uint64 `json:"guid"`
// ID
ID uint64 `json:"id"`
// Location code
LocationCode string `json:"locationCode"`
// Name
Name string `json:"name"`
}
// Information about grid
type ItemGridList struct {
// Resource information
Resources Resources `json:"Resources"`
@@ -92,7 +113,7 @@ type RecordGrid struct {
// List Grids
type ListGrids struct {
//Data
Data []RecordGrid `json:"data"`
Data []ItemGridList `json:"data"`
// Entry count
EntryCount uint64 `json:"entryCount"`

View File

@@ -29,7 +29,7 @@ type CreateRequest struct {
// Start now Load balancer
// Required: false
Start bool `url:"start" json:"start" validate:"required"`
Start bool `url:"start" json:"start"`
// Text description of this load balancer
// Required: false

View File

@@ -9,7 +9,7 @@ import (
// FilterByID returns ListLB with specified ID.
func (ll ListLB) FilterByID(id uint64) ListLB {
predicate := func(rlb RecordLB) bool {
predicate := func(rlb ItemLBList) bool {
return rlb.ID == id
}
@@ -18,7 +18,7 @@ func (ll ListLB) FilterByID(id uint64) ListLB {
// FilterByName returns ListLB with specified Name.
func (ll ListLB) FilterByName(name string) ListLB {
predicate := func(rlb RecordLB) bool {
predicate := func(rlb ItemLBList) bool {
return rlb.Name == name
}
@@ -27,22 +27,13 @@ func (ll ListLB) FilterByName(name string) ListLB {
// FilterByExtNetID returns ListLB with specified ExtNetID.
func (ll ListLB) FilterByExtNetID(extNetID uint64) ListLB {
predicate := func(rlb RecordLB) bool {
predicate := func(rlb ItemLBList) bool {
return rlb.ExtNetID == extNetID
}
return ll.FilterFunc(predicate)
}
// FilterByImageID returns ListLB with specified ImageID.
func (ll ListLB) FilterByImageID(imageID uint64) ListLB {
predicate := func(rlb RecordLB) bool {
return rlb.ImageID == imageID
}
return ll.FilterFunc(predicate)
}
// FilterByK8SID returns ListLB used by specified K8S cluster.
func (ll ListLB) FilterByK8SID(ctx context.Context, k8sID uint64, decortClient interfaces.Caller) (*ListLB, error) {
caller := k8s.New(decortClient)
@@ -56,7 +47,7 @@ func (ll ListLB) FilterByK8SID(ctx context.Context, k8sID uint64, decortClient i
return nil, err
}
predicate := func(rlb RecordLB) bool {
predicate := func(rlb ItemLBList) bool {
return cluster.LBID == rlb.ID
}
@@ -66,7 +57,7 @@ func (ll ListLB) FilterByK8SID(ctx context.Context, k8sID uint64, decortClient i
}
// FilterFunc allows filtering ListLB based on a user-specified predicate.
func (ll ListLB) FilterFunc(predicate func(RecordLB) bool) ListLB {
func (ll ListLB) FilterFunc(predicate func(ItemLBList) bool) ListLB {
var result ListLB
for _, item := range ll.Data {
@@ -82,9 +73,9 @@ func (ll ListLB) FilterFunc(predicate func(RecordLB) bool) ListLB {
// FindOne returns first found RecordLB
// If none was found, returns an empty struct.
func (ll ListLB) FindOne() RecordLB {
func (ll ListLB) FindOne() ItemLBList {
if len(ll.Data) == 0 {
return RecordLB{}
return ItemLBList{}
}
return ll.Data[0]

View File

@@ -3,11 +3,9 @@ package lb
import "testing"
var lbs = ListLB{
Data: []RecordLB{
Data: []ItemLBList{
{
HAMode: true,
CKey: "",
Meta: []interface{}{},
ACL: []interface{}{},
Backends: []ItemBackend{},
CreatedBy: "test_user_1",
@@ -22,7 +20,6 @@ var lbs = ListLB{
GID: 212,
GUID: 1,
ID: 1,
ImageID: 2121,
Milestones: 129000,
Name: "k8s-lb-test-1",
PrimaryNode: Node{},
@@ -37,8 +34,6 @@ var lbs = ListLB{
},
{
HAMode: false,
CKey: "",
Meta: []interface{}{},
ACL: []interface{}{},
Backends: []ItemBackend{},
CreatedBy: "test_user_2",
@@ -53,7 +48,6 @@ var lbs = ListLB{
GID: 212,
GUID: 2,
ID: 2,
ImageID: 2129,
Milestones: 129013,
Name: "k8s-lb-test-2",
PrimaryNode: Node{},
@@ -68,8 +62,6 @@ var lbs = ListLB{
},
{
HAMode: true,
CKey: "",
Meta: []interface{}{},
ACL: []interface{}{},
Backends: []ItemBackend{},
CreatedBy: "te2t_user_3",
@@ -84,7 +76,6 @@ var lbs = ListLB{
GID: 212,
GUID: 3,
ID: 3,
ImageID: 2139,
Milestones: 129025,
Name: "k8s-lb-test-3",
PrimaryNode: Node{},
@@ -125,16 +116,8 @@ func TestFilterByExtNetID(t *testing.T) {
}
}
func TestFilterByImageID(t *testing.T) {
actual := lbs.FilterByImageID(2139).FindOne()
if actual.ImageID != 2139 {
t.Fatal("expected ImageID 2139, found: ", actual.ImageID)
}
}
func TestFilterFunc(t *testing.T) {
actual := lbs.FilterFunc(func(rl RecordLB) bool {
actual := lbs.FilterFunc(func(rl ItemLBList) bool {
return rl.Status == "DISABLED"
})

View File

@@ -136,7 +136,7 @@ type Node struct {
// List of load balancers
type ListLB struct {
// Data
Data []RecordLB `json:"data"`
Data []ItemLBList `json:"data"`
// Entry count
EntryCount uint64 `json:"entryCount"`
@@ -159,6 +159,69 @@ type RecordLB struct {
// List of load balancer backends
Backends ListBackends `json:"backends"`
// Description
Description string `json:"desc"`
// DPAPI password
DPAPIPassword string `json:"dpApiPassword"`
// DPAPI user
DPAPIUser string `json:"dpApiUser"`
// External network ID
ExtNetID uint64 `json:"extnetId"`
// List of load balancer frontends
Frontends ListFrontends `json:"frontends"`
// Grid ID
GID uint64 `json:"gid"`
// GUID
GUID uint64 `json:"guid"`
// ID
ID uint64 `json:"id"`
// Image ID
ImageID uint64 `json:"imageId"`
// Milestones
Milestones uint64 `json:"milestones"`
// Name
Name string `json:"name"`
// Primary node
PrimaryNode Node `json:"primaryNode"`
// Resource group ID
RGID uint64 `json:"rgId"`
// Secondary node
SecondaryNode Node `json:"secondaryNode"`
// Status
Status string `json:"status"`
// Tech status
TechStatus string `json:"techStatus"`
// VINS ID
VINSID uint64 `json:"vinsId"`
}
// Detailed information about load balancer in List
type ItemLBList struct {
// HAMode
HAMode bool `json:"HAmode"`
// Access Control List
ACL []interface{} `json:"acl"`
// List of load balancer backends
Backends ListBackends `json:"backends"`
// Created by
CreatedBy string `json:"createdBy"`
@@ -195,9 +258,6 @@ type RecordLB struct {
// ID
ID uint64 `json:"id"`
// Image ID
ImageID uint64 `json:"imageId"`
// Milestones
Milestones uint64 `json:"milestones"`

View File

@@ -24,10 +24,31 @@ type Total struct {
UsageLimit uint64 `json:"usage_limit"`
}
type ByPool struct {
// Disk count
DiskCount uint64 `json:"disk_count"`
// Disk usage
DiskUsage uint64 `json:"disk_usage"`
// Snapshot count
SnapshotCount uint64 `json:"snapshot_count"`
// Snapshot usage
SnapshotUsage uint64 `json:"snapshot_usage"`
// Usage
Usage uint64 `json:"usage"`
// Usage limit
UsageLimit uint64 `json:"usage_limit"`
}
// Main information about consumption
type RecordConsumption struct {
// By pool
ByPool map[string]interface{} `json:"byPool"`
ByPool map[string]ByPool `json:"byPool"`
// Total resource information
Total Total `json:"total"`
@@ -70,6 +91,9 @@ type RecordPool struct {
// List URIs
URIs ListURIs `json:"uris"`
// Usage Limit
UsageLimit uint64 `json:"usage_limit"`
}
// SEP config