From e6440bc4a3bb8cbf57d0a24755df8b5c1836b173 Mon Sep 17 00:00:00 2001 From: Nikita Sorokin Date: Tue, 7 Nov 2023 15:54:13 +0300 Subject: [PATCH] v1.6.9 --- CHANGELOG.md | 8 +- client.go | 4 +- client_bvs.go | 457 +++++++++++++++++++++++++ config/config_bvs.go | 116 +++++++ go.mod | 11 +- go.sum | 35 +- legacy-client.go | 2 +- pkg/cloudapi/bservice/models.go | 11 +- pkg/cloudapi/bservice/snapshot_list.go | 6 +- pkg/cloudapi/rg/ids.go | 9 + pkg/cloudapi/rg/models.go | 12 +- 11 files changed, 651 insertions(+), 20 deletions(-) create mode 100644 client_bvs.go create mode 100644 config/config_bvs.go diff --git a/CHANGELOG.md b/CHANGELOG.md index ab80af3..f49acd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,9 @@ -## Version 1.6.8 +## Version 1.7.0 ## Feature -- Add IDs() methods returning array of uint64 IDs for all List* structs in cloudapi/cloudbroker groups +- Added support for authorization using the Basis.Virtual Security system. Add client and config ## Bugfix -- Fix field Audit in CloudBrokerEndpoints model in cloudbroker/apiaccess +- Add model ListInfoSnapshots in cloudapi/bservice/models +- Fix func SnapshotList for work with new model cloudapi/bservice/snapshot_list +- Add models ItemAffinityGroup, ListAffinityGroup in cloudapi/rg/models \ No newline at end of file diff --git a/client.go b/client.go index 6866709..7369da7 100644 --- a/client.go +++ b/client.go @@ -22,6 +22,8 @@ import ( k8s_cb "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/k8s" ) +const restmachine = "/restmachine" + // HTTP-client for platform type DecortClient struct { decortURL string @@ -88,7 +90,7 @@ func (dc *DecortClient) DecortApiCall(ctx context.Context, method, url string, p body = bytes.NewBufferString(values.Encode()) } - req, err := http.NewRequestWithContext(ctx, method, dc.decortURL+"/restmachine"+url, body) + req, err := http.NewRequestWithContext(ctx, method, dc.decortURL+restmachine+url, body) if err != nil { return nil, err } diff --git a/client_bvs.go b/client_bvs.go new file mode 100644 index 0000000..f86bee9 --- /dev/null +++ b/client_bvs.go @@ -0,0 +1,457 @@ +package decortsdk + +import ( + "bytes" + "context" + "crypto/tls" + "encoding/json" + "errors" + "fmt" + "io" + "mime/multipart" + "net/http" + "strconv" + "strings" + "sync" + + "github.com/google/go-querystring/query" + "golang.org/x/oauth2" + "repository.basistech.ru/BASIS/decort-golang-sdk/config" + "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi" + k8s_ca "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/k8s" + "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker" + k8s_cb "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/k8s" +) + +// HTTP-client for platform +type BVSDecortClient struct { + client *http.Client + cfg *oauth2.Config + mutex *sync.Mutex + token *oauth2.Token + decortURL string + username string + password string +} + +type ProviderEndpoint struct { + TokenURL string `json:"token_endpoint"` +} + +// Сlient builder +func NewBVS(cfg config.BVSConfig) *BVSDecortClient { + if cfg.Retries == 0 { + cfg.Retries = 5 + } + // if cfg.Token.AccessToken != "" { + + // } + ctx := context.Background() + providerEndpoint, _ := GetEndpoint(ctx, cfg.SSOURL, cfg.Domain, cfg.SSLSkipVerify) + + return &BVSDecortClient{ + decortURL: cfg.DecortURL, + client: &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + //nolint:gosec + InsecureSkipVerify: cfg.SSLSkipVerify, + }, + }, + }, + cfg: &oauth2.Config{ + ClientID: cfg.AppID, + ClientSecret: cfg.AppSecret, + Endpoint: providerEndpoint, + }, + mutex: &sync.Mutex{}, + token: &cfg.Token, + username: cfg.Username, + password: cfg.Password, + } +} + +// CloudAPI builder +func (bdc *BVSDecortClient) CloudAPI() *cloudapi.CloudAPI { + return cloudapi.New(bdc) +} + +// CloudBroker builder +func (bdc *BVSDecortClient) CloudBroker() *cloudbroker.CloudBroker { + return cloudbroker.New(bdc) +} + +// DecortApiCall method for sending requests to the platform +func (bdc *BVSDecortClient) DecortApiCall(ctx context.Context, method, url string, params interface{}) ([]byte, error) { + k8sCaCreateReq, okCa := params.(k8s_ca.CreateRequest) + k8sCbCreateReq, okCb := params.(k8s_cb.CreateRequest) + var body *bytes.Buffer + var ctype string + + if okCa { + body, ctype = createK8sCloudApiBVS(k8sCaCreateReq) + } else if okCb { + body, ctype = createK8sCloudBrokerBVS(k8sCbCreateReq) + } else { + values, err := query.Values(params) + if err != nil { + return nil, err + } + body = bytes.NewBufferString(values.Encode()) + } + + req, err := http.NewRequestWithContext(ctx, method, bdc.decortURL+restmachine+url, body) + if err != nil { + return nil, err + } + + if err = bdc.getToken(ctx); err != nil { + return nil, err + } + + resp, err := bdc.do(req, ctype) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + respBytes, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + if resp.StatusCode != 200 { + return nil, errors.New(string(respBytes)) + } + + return respBytes, nil +} + +func (bdc *BVSDecortClient) getToken(ctx context.Context) error { + bdc.mutex.Lock() + defer bdc.mutex.Unlock() + + if !bdc.token.Valid() { + + body := fmt.Sprintf("grant_type=password&client_id=%s&client_secret=%s&username=%s&password=%s&response_type=token", bdc.cfg.ClientID, bdc.cfg.ClientSecret, bdc.username, bdc.password) + bodyReader := strings.NewReader(body) + // body := fmt.Sprintf("grant_type=client_credentials&client_id=%s&client_secret=%s&", bdc.cfg.ClientID, bdc.cfg.ClientSecret) + // bodyReader := strings.NewReader(body) + + req, _ := http.NewRequestWithContext(ctx, "POST", bdc.cfg.Endpoint.TokenURL, bodyReader) + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + + resp, err := bdc.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) + } + + err = json.Unmarshal(tokenBytes, &bdc.token) + if err != nil { + return fmt.Errorf("cannot unmarshal token: %s", tokenBytes) + } + } + + return nil +} + +func (bdc *BVSDecortClient) do(req *http.Request, ctype string) (*http.Response, error) { + if ctype != "" { + req.Header.Add("Content-Type", ctype) + } else { + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + } + bdc.token.SetAuthHeader(req) + req.Header.Set("Accept", "application/json") + + // var resp *http.Response + // var err error + buf, _ := io.ReadAll(req.Body) + // req = req.Clone(req.Context()) + + // for i := uint64(0); i < dc.cfg.Retries; i++ { + req.Body = io.NopCloser(bytes.NewBuffer(buf)) + resp, err := bdc.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) +} + +func createK8sCloudApiBVS(req k8s_ca.CreateRequest) (*bytes.Buffer, string) { + reqBody := &bytes.Buffer{} + writer := multipart.NewWriter(reqBody) + if req.OidcCertificate != "" { + part, _ := writer.CreateFormFile("oidcCertificate", "ca.crt") + _, _ = io.Copy(part, strings.NewReader(req.OidcCertificate)) + } + + _ = writer.WriteField("name", req.Name) + _ = writer.WriteField("rgId", strconv.FormatUint(req.RGID, 10)) + _ = writer.WriteField("k8ciId", strconv.FormatUint(req.K8SCIID, 10)) + _ = writer.WriteField("workerGroupName", req.WorkerGroupName) + _ = writer.WriteField("networkPlugin", req.NetworkPlugin) + + if req.MasterSEPID != 0 { + _ = writer.WriteField("masterSepId", strconv.FormatUint(req.MasterSEPID, 10)) + } + if req.MasterSEPPool != "" { + _ = writer.WriteField("masterSepPool", req.MasterSEPPool) + } + if req.WorkerSEPID != 0 { + _ = writer.WriteField("workerSepId", strconv.FormatUint(req.WorkerSEPID, 10)) + } + if req.WorkerSEPPool != "" { + _ = writer.WriteField("workerSepPool", req.WorkerSEPPool) + } + + if req.Labels != nil { + for _, v := range req.Labels { + _ = writer.WriteField("labels", v) + } + } + if req.Taints != nil { + for _, v := range req.Taints { + _ = writer.WriteField("taints", v) + } + } + if req.Annotations != nil { + for _, v := range req.Annotations { + _ = writer.WriteField("annotations", v) + } + } + + if req.MasterCPU != 0 { + _ = writer.WriteField("masterCpu", strconv.FormatUint(uint64(req.MasterCPU), 10)) + } + if req.MasterNum != 0 { + _ = writer.WriteField("masterNum", strconv.FormatUint(uint64(req.MasterNum), 10)) + } + if req.MasterRAM != 0 { + _ = writer.WriteField("masterRam", strconv.FormatUint(uint64(req.MasterRAM), 10)) + } + if req.MasterDisk != 0 { + _ = writer.WriteField("masterDisk", strconv.FormatUint(uint64(req.MasterDisk), 10)) + } + if req.WorkerCPU != 0 { + _ = writer.WriteField("workerCpu", strconv.FormatUint(uint64(req.WorkerCPU), 10)) + } + if req.WorkerNum != 0 { + _ = writer.WriteField("workerNum", strconv.FormatUint(uint64(req.WorkerNum), 10)) + } + if req.WorkerRAM != 0 { + _ = writer.WriteField("workerRam", strconv.FormatUint(uint64(req.WorkerRAM), 10)) + } + if req.WorkerDisk != 0 { + _ = writer.WriteField("workerDisk", strconv.FormatUint(uint64(req.WorkerDisk), 10)) + } + if req.ExtNetID != 0 { + _ = writer.WriteField("extnetId", strconv.FormatUint(req.ExtNetID, 10)) + } + if req.VinsId != 0 { + _ = writer.WriteField("vinsId", strconv.FormatUint(req.VinsId, 10)) + } + if !req.WithLB { + _ = writer.WriteField("withLB", strconv.FormatBool(req.WithLB)) + } + + _ = writer.WriteField("highlyAvailableLB", strconv.FormatBool(req.HighlyAvailable)) + + if req.AdditionalSANs != nil { + for _, v := range req.AdditionalSANs { + _ = writer.WriteField("additionalSANs", v) + } + } + if req.InitConfiguration != "" { + _ = writer.WriteField("initConfiguration", req.InitConfiguration) + } + if req.ClusterConfiguration != "" { + _ = writer.WriteField("clusterConfiguration", req.ClusterConfiguration) + } + if req.KubeletConfiguration != "" { + _ = writer.WriteField("kubeletConfiguration", req.KubeletConfiguration) + } + if req.KubeProxyConfiguration != "" { + _ = writer.WriteField("kubeProxyConfiguration", req.KubeProxyConfiguration) + } + if req.JoinConfiguration != "" { + _ = writer.WriteField("joinConfiguration", req.JoinConfiguration) + } + if req.Description != "" { + _ = writer.WriteField("desc", req.Description) + } + if req.UserData != "" { + _ = writer.WriteField("userData", req.UserData) + } + + _ = writer.WriteField("extnetOnly", strconv.FormatBool(req.ExtNetOnly)) + + ct := writer.FormDataContentType() + writer.Close() + + return reqBody, ct +} + +func createK8sCloudBrokerBVS(req k8s_cb.CreateRequest) (*bytes.Buffer, string) { + reqBody := &bytes.Buffer{} + writer := multipart.NewWriter(reqBody) + if req.OidcCertificate != "" { + part, _ := writer.CreateFormFile("oidcCertificate", "ca.crt") + _, _ = io.Copy(part, strings.NewReader(req.OidcCertificate)) + } + + _ = writer.WriteField("name", req.Name) + _ = writer.WriteField("rgId", strconv.FormatUint(req.RGID, 10)) + _ = writer.WriteField("k8ciId", strconv.FormatUint(req.K8CIID, 10)) + _ = writer.WriteField("workerGroupName", req.WorkerGroupName) + _ = writer.WriteField("networkPlugin", req.NetworkPlugin) + + if req.MasterSEPID != 0 { + _ = writer.WriteField("masterSepId", strconv.FormatUint(req.MasterSEPID, 10)) + } + if req.MasterSEPPool != "" { + _ = writer.WriteField("masterSepPool", req.MasterSEPPool) + } + if req.WorkerSEPID != 0 { + _ = writer.WriteField("workerSepId", strconv.FormatUint(req.WorkerSEPID, 10)) + } + if req.WorkerSEPPool != "" { + _ = writer.WriteField("workerSepPool", req.WorkerSEPPool) + } + + if req.Labels != nil { + for _, v := range req.Labels { + _ = writer.WriteField("labels", v) + } + } + if req.Taints != nil { + for _, v := range req.Taints { + _ = writer.WriteField("taints", v) + } + } + if req.Annotations != nil { + for _, v := range req.Annotations { + _ = writer.WriteField("annotations", v) + } + } + + if req.MasterCPU != 0 { + _ = writer.WriteField("masterCpu", strconv.FormatUint(req.MasterCPU, 10)) + } + if req.MasterNum != 0 { + _ = writer.WriteField("masterNum", strconv.FormatUint(req.MasterNum, 10)) + } + if req.MasterRAM != 0 { + _ = writer.WriteField("masterRam", strconv.FormatUint(req.MasterRAM, 10)) + } + if req.MasterDisk != 0 { + _ = writer.WriteField("masterDisk", strconv.FormatUint(req.MasterDisk, 10)) + } + if req.WorkerCPU != 0 { + _ = writer.WriteField("workerCpu", strconv.FormatUint(req.WorkerCPU, 10)) + } + if req.WorkerNum != 0 { + _ = writer.WriteField("workerNum", strconv.FormatUint(req.WorkerNum, 10)) + } + if req.WorkerRAM != 0 { + _ = writer.WriteField("workerRam", strconv.FormatUint(req.WorkerRAM, 10)) + } + if req.WorkerDisk != 0 { + _ = writer.WriteField("workerDisk", strconv.FormatUint(req.WorkerDisk, 10)) + } + if req.ExtNetID != 0 { + _ = writer.WriteField("extnetId", strconv.FormatUint(req.ExtNetID, 10)) + } + if req.VinsId != 0 { + _ = writer.WriteField("vinsId", strconv.FormatUint(req.VinsId, 10)) + } + if !req.WithLB { + _ = writer.WriteField("withLB", strconv.FormatBool(req.WithLB)) + } + + _ = writer.WriteField("highlyAvailableLB", strconv.FormatBool(req.HighlyAvailable)) + + if req.AdditionalSANs != nil { + for _, v := range req.AdditionalSANs { + _ = writer.WriteField("additionalSANs", v) + } + } + if req.InitConfiguration != "" { + _ = writer.WriteField("initConfiguration", req.InitConfiguration) + } + if req.ClusterConfiguration != "" { + _ = writer.WriteField("clusterConfiguration", req.ClusterConfiguration) + } + if req.KubeletConfiguration != "" { + _ = writer.WriteField("kubeletConfiguration", req.KubeletConfiguration) + } + if req.KubeProxyConfiguration != "" { + _ = writer.WriteField("kubeProxyConfiguration", req.KubeProxyConfiguration) + } + if req.JoinConfiguration != "" { + _ = writer.WriteField("joinConfiguration", req.JoinConfiguration) + } + if req.Description != "" { + _ = writer.WriteField("desc", req.Description) + } + if req.UserData != "" { + _ = writer.WriteField("userData", req.UserData) + } + + _ = writer.WriteField("extnetOnly", strconv.FormatBool(req.ExtNetOnly)) + + ct := writer.FormDataContentType() + + writer.Close() + return reqBody, ct +} + +func GetEndpoint(ctx context.Context, issuer string, domain string, skip bool) (oauth2.Endpoint, error) { + wellKnown := issuer + "/" + domain + "/.well-known/openid-configuration" + req, err := http.NewRequestWithContext(ctx, "GET", wellKnown, nil) + if err != nil { + return oauth2.Endpoint{}, err + } + + client := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + //nolint:gosec + InsecureSkipVerify: skip, + }, + }, + } + + resp, err := client.Do(req) + if err != nil { + return oauth2.Endpoint{}, err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return oauth2.Endpoint{}, fmt.Errorf("unable to read response body: %w", err) + } + + var p ProviderEndpoint + err = json.Unmarshal(body, &p) + if err != nil { + return oauth2.Endpoint{}, fmt.Errorf("cannot unmarshal endpoint: %s", body) + } + + return oauth2.Endpoint{TokenURL: p.TokenURL}, nil +} diff --git a/config/config_bvs.go b/config/config_bvs.go new file mode 100644 index 0000000..fc0e507 --- /dev/null +++ b/config/config_bvs.go @@ -0,0 +1,116 @@ +package config + +import ( + "encoding/json" + "os" + "time" + + "golang.org/x/oauth2" + "gopkg.in/yaml.v3" + "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" +) + +type BVSConfig struct { + // ServiceAccount username + // Required: true + // Example : "osh_mikoev" + Username string `json:"username" yaml:"username" validate:"required"` + + // ServiceAccount password + // Required: true + // Example: "[1o>hYkjnJr)HI78q7t&#%8Lm" + Password string `json:"password" yaml:"password" validate:"required"` + + // Domain name + // Required: true + // Example: "realms/dynamix" + Domain string `json:"domain" yaml:"domain"` + + // Application (client) identifier for authorization + // in the cloud platform controller in oauth2 mode. + // Required: true + // Example: "ewqfrvea7s890avw804389qwguf234h0otfi3w4eiu" + AppID string `json:"appId" yaml:"appId" validate:"required"` + + // Application (client) secret code for authorization + // in the cloud platform controller in oauth2 mode. + // Example: "frvet09rvesfis0c9erv9fsov0vsdfi09ovds0f" + AppSecret string `json:"appSecret" yaml:"appSecret" validate:"required"` + + // Platform authentication service address + // Required: true + // Example: "https://sso.digitalenergy.online" + SSOURL string `json:"ssoUrl" yaml:"ssoUrl" validate:"url"` + + // The address of the platform on which the actions are planned + // Required: true + // Example: "https://mr4.digitalenergy.online" + DecortURL string `json:"decortUrl" yaml:"decortUrl" validate:"url"` + + // JWT platform token + // Required: false + // Example: "qwqwdfwv68979we0q9bfv7e9sbvd89798qrwv97ff" + Token oauth2.Token `json:"token" yaml:"token"` + + // Amount platform request attempts + // Default value: 5 + // Required: false + Retries uint64 `json:"retries" yaml:"retries"` + + // Skip verify + // Required: false + SSLSkipVerify bool `json:"sslSkipVerify" yaml:"sslSkipVerify"` + + // HTTP client timeout, unlimited if left empty + // Required: false + Timeout Duration `json:"timeout" yaml:"timeout"` +} + +// SetTimeout is used to set HTTP client timeout. +func (c *BVSConfig) SetTimeout(dur time.Duration) { + c.Timeout = Duration(dur) +} + +// ParseConfigJSON parses Config from specified JSON-formatted file. +func ParseConfigBVSJSON(path string) (BVSConfig, error) { + file, err := os.ReadFile(path) + if err != nil { + return BVSConfig{}, err + } + + var config BVSConfig + + err = json.Unmarshal(file, &config) + if err != nil { + return BVSConfig{}, err + } + + err = validators.ValidateConfig(config) + if err != nil { + return BVSConfig{}, validators.ValidationErrors(validators.GetErrors(err)) + } + + return config, nil +} + +// ParseConfigYAML parses Config from specified YAML-formatted file. +func ParseConfigBVSYAML(path string) (BVSConfig, error) { + file, err := os.ReadFile(path) + if err != nil { + return BVSConfig{}, err + } + + var config BVSConfig + + err = yaml.Unmarshal(file, &config) + if err != nil { + return BVSConfig{}, err + } + + err = validators.ValidateConfig(config) + if err != nil { + return BVSConfig{}, validators.ValidationErrors(validators.GetErrors(err)) + } + + return config, nil +} diff --git a/go.mod b/go.mod index beb7e0e..7588a3e 100644 --- a/go.mod +++ b/go.mod @@ -5,15 +5,20 @@ go 1.20 require ( github.com/go-playground/validator/v10 v10.11.2 github.com/google/go-querystring v1.1.0 + golang.org/x/oauth2 v0.13.0 gopkg.in/yaml.v3 v3.0.1 ) require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/kr/text v0.2.0 // indirect github.com/leodido/go-urn v1.2.1 // indirect - golang.org/x/crypto v0.5.0 // indirect - golang.org/x/sys v0.4.0 // indirect - golang.org/x/text v0.6.0 // indirect + golang.org/x/crypto v0.14.0 // indirect + golang.org/x/net v0.16.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/protobuf v1.31.0 // indirect ) diff --git a/go.sum b/go.sum index a641cd7..95dee0d 100644 --- a/go.sum +++ b/go.sum @@ -8,8 +8,13 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU= github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s= -github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= @@ -23,13 +28,29 @@ github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUA github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= -golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= -golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos= +golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= +golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +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/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/legacy-client.go b/legacy-client.go index df408b4..066644f 100644 --- a/legacy-client.go +++ b/legacy-client.go @@ -94,7 +94,7 @@ func (ldc *LegacyDecortClient) DecortApiCall(ctx context.Context, method, url st body = bytes.NewBufferString(values.Encode() + fmt.Sprintf("&authkey=%s", ldc.cfg.Token)) } - req, err := http.NewRequestWithContext(ctx, method, ldc.decortURL+"/restmachine"+url, body) + req, err := http.NewRequestWithContext(ctx, method, ldc.decortURL+restmachine+url, body) if err != nil { return nil, err } diff --git a/pkg/cloudapi/bservice/models.go b/pkg/cloudapi/bservice/models.go index 4432780..75efa33 100644 --- a/pkg/cloudapi/bservice/models.go +++ b/pkg/cloudapi/bservice/models.go @@ -168,9 +168,18 @@ type ItemSnapshot struct { Valid bool `json:"valid"` } -// List of Snapshots +// List of Snapshot type ListSnapshots []ItemSnapshot +// List of Snapshots +type ListInfoSnapshots struct { + // Data + Data ListSnapshots `json:"data"` + + // EntryCount + EntryCount uint64 `json:"entryCount"` +} + // Main information about Group type RecordGroup struct { // Account ID diff --git a/pkg/cloudapi/bservice/snapshot_list.go b/pkg/cloudapi/bservice/snapshot_list.go index efeba00..207a10f 100644 --- a/pkg/cloudapi/bservice/snapshot_list.go +++ b/pkg/cloudapi/bservice/snapshot_list.go @@ -16,7 +16,7 @@ type SnapshotListRequest struct { } // SnapshotList gets list existing snapshots of the Basic Service -func (b BService) SnapshotList(ctx context.Context, req SnapshotListRequest) (ListSnapshots, error) { +func (b BService) SnapshotList(ctx context.Context, req SnapshotListRequest) (*ListInfoSnapshots, error) { err := validators.ValidateRequest(req) if err != nil { return nil, validators.ValidationErrors(validators.GetErrors(err)) @@ -29,12 +29,12 @@ func (b BService) SnapshotList(ctx context.Context, req SnapshotListRequest) (Li return nil, err } - list := ListSnapshots{} + list := ListInfoSnapshots{} err = json.Unmarshal(res, &list) if err != nil { return nil, err } - return list, nil + return &list, nil } diff --git a/pkg/cloudapi/rg/ids.go b/pkg/cloudapi/rg/ids.go index 3a875af..457535c 100644 --- a/pkg/cloudapi/rg/ids.go +++ b/pkg/cloudapi/rg/ids.go @@ -53,3 +53,12 @@ func (lrc ListResourceConsumption) IDs() []uint64 { } return res } + +// IDs gets array of ResourceGroupIDs from ListAffinityGroup struct +func (lag ListAffinityGroup) IDs() []uint64 { + res := make([]uint64, 0, len(lag)) + for _, ag := range lag { + res = append(res, ag.ID) + } + return res +} diff --git a/pkg/cloudapi/rg/models.go b/pkg/cloudapi/rg/models.go index 71849ec..cf1bef4 100644 --- a/pkg/cloudapi/rg/models.go +++ b/pkg/cloudapi/rg/models.go @@ -331,9 +331,19 @@ type ItemAffinityGroupComputes struct { // List of affinity groups type ListAffinityGroupsComputes []ItemAffinityGroupComputes +// Main information about +type ItemAffinityGroup struct { + ID uint64 `json:"id"` + NodeID uint64 `json:"node_id"` +} + +// List of affinity group +type ListAffinityGroup []ItemAffinityGroup + +// List of affinity groups type ListAffinityGroups struct { // Data - Data []map[string][]uint64 `json:"data"` + Data []map[string]ListAffinityGroup `json:"data"` // Entry count EntryCount uint64 `json:"entryCount"`