Compare commits

...

18 Commits

Author SHA1 Message Date
80491ed643 v1.9.0 2024-11-12 12:51:21 +03:00
f1e0f7abb6 v1.8.3 2024-08-26 17:51:34 +03:00
8eeef825c0 v1.8.2 2024-06-28 10:54:20 +03:00
9a7a7b6f36 v1.8.1 2024-06-05 12:42:19 +03:00
3393934456 v1.8.1 2024-05-31 13:35:39 +03:00
e7c968797b v1.8.0 2024-04-16 14:26:06 +03:00
bc264c4d90 v1.7.7 2024-03-14 14:58:32 +03:00
d137c7507a v1.7.6 2024-03-14 14:52:56 +03:00
55027a1605 v1.6.13 2024-03-14 10:17:08 +03:00
de9cca4053 v1.7.6 2024-03-06 17:02:50 +03:00
83bf1fb1fa v1.6.12 2024-03-06 16:50:27 +03:00
16cad7a3e7 v1.7.5 2024-02-01 10:50:38 +03:00
96273d2a12 v1.7.4 2024-01-22 13:10:00 +03:00
d4065938ac v1.7.3 2023-12-18 16:27:15 +03:00
cd741b7f11 v1.7.2 2023-11-29 15:57:26 +03:00
a85ad3acd5 v1.7.1 2023-11-15 12:03:31 +03:00
823dfb49bc v1.7.0 2023-11-09 10:27:14 +03:00
e6440bc4a3 v1.6.9 2023-11-07 15:54:13 +03:00
497 changed files with 15699 additions and 2713 deletions

4
.gitignore vendored
View File

@@ -3,3 +3,7 @@ cmd/
.vscode/ .vscode/
.fleet/ .fleet/
.DS_Store .DS_Store
tests/platform_upgrade/.env
tests/platform_upgrade/input.json
tests/platform_upgrade/*.txt
*.env

View File

@@ -1,7 +1,131 @@
## Version 1.6.8 ## Version 1.9.0
## Feature ### Feature
- Add IDs() methods returning array of uint64 IDs for all List* structs in cloudapi/cloudbroker groups
## Bugfix #### account
- Fix field Audit in CloudBrokerEndpoints model in cloudbroker/apiaccess - Delete field RecursiveDelete in DeleteUserRequest in cloudapi/account and cloudbroker/account
- Add field UniqPools in UpdateRequest in cloudapi/account
- Add field ClearUniqPools in UpdateRequest in cloudbroker/account
- Add field SortBy in listFlipGroups in cloudapi/account
- Change Ratio field from required to optional in SetCPUAllocationRatioRequest in cloudbroker/compute
#### audit
- Add endpoint Get in cloudapi/audit
- Add endpoint ExportAuditsToFile in cloudbroker/audit
- Delete field Apitask in model RecordAudit in cloudbroker/audit
#### compute
- Add support DPDK network net type in NetAttach endpoints in cloudapi/compute and cloudbroker/compute
- Add field Force in CloneRequest in cloudapi/compute and cloudbroker/compute
- Change TargetStackID field from required to optional (add default 0 value) in PinToStackRequest in cloudbroker/compute
- Change return value type from bool to string in SnapshotDelete in cloudbroker/compute and cloudapi/compute
- Add field StatusCheck in MigrateStorageAbortRequest in cloudbroker/compute
- Add function SnapshotDeleteAsync in cloudapi/compute and cloudbroker/compute
- Add field StackID in ListRequest in cloudbroker/compute
- Add field NodeID in model RecordCompute in cloudbroker/compute
- Add function SetNetConfig in cloudbroker/compute
- Add field MTU in model RecordNetAttach in cloudapi/compute and cloudbroker/compute
- Change Mode field from required to optional in DiskMigrateRequest in cloudapi/compute
- Add field Chipset in model RecordCompute in cloudapi/compute and cloudbroker/compute
- Add field BusNumber in model RecordCompute, ItemComputeDisk, ItemDisk, InfoDisk in cloudapi/compute and cloudbroker/compute
- Add field ImageID in ListRequest in cloudbroker/compute
- Change Mode field from required to optional in DiskMigrateRequest in cloudbroker/compute
- Add field Chipset in ListRequest in cloudbroker/compute
- Add endpoints ChangeIP in cloudapi/compute and cloudbroker/compute
#### disks
- Change drivers field from optional to required in FromPlatformDiskRequest in cloudapi/disks, cloudbroker/disks
#### dpdknet
- Add endpoints List, Get in cloudapi/dpdk
- Add endpoints Create, Delete, Disable Enable, Get, List, Update in cloudbroker/dpdk
#### extnet
- Change StackId field from required to optional in DeviceMigrateRequest in cloudbroker/extnet
#### flipgroup
- Change ClientIDs field from []uint64 to []string in ListRequest, cloudapi/flipgroup
#### grid
- Change PasswordLength field from required to optional (add default 9 value) in SetPasswordPolicyRequest in cloudbroker/grid
- Set Default value (true) in fields Digits and Uppercase in SetPasswordPolicyRequest in cloudbroker/grid
- Add fields ckey, meta in model RecordGrid in cloudbroker/grid
- Change GID field from uint64 to string in ExecuteMaintenanceScriptRequest in cloudbroker/grid
- Add endpoints GetSettings and GetSettingsRaw in cloudbroker/grid
#### group
- Change StackId field from uint64 to string in ListRequest in cloudbroker/group
#### image:
- Change Drivers field from required to optional in CreateCDROMImageRequest in cloudbroker/image
- Add field Enabled in ListRequest in cloudapi/image and cloudbroker/image
- Delete field GID in SyncCreateRequest in cloudbroker/image
- Delete field Permanently in DeleteRequest in cloudapi/image and cloudbroker/image
- Delete field Permanently in DeleteImagesRequest in cloudbroker/image
#### k8s
- Add fields HighlyAvailableLB, AddressVip, ExtnetOnly, WithLB in model RecordK8S in cloudbroker/k8s and cloudapi/k8s
- Change MasterIds field from a slice of string to a slice of integers in DeleteMasterFromGroupRequest in cloudbroker/k8s and cloudapi/k8s
- Add field Chipset in WorkerAddRequest, WorkersGroupAddRequest in cloudapi/k8s, cloudbroker/k8s
- Add field Chipset in CreateRequest in cloudapi/k8s, cloudbroker/k8s
#### kvmx86
- Add support DPDK network interface in Create endpoints in cloudapi/kvmx86 and cloudbroker/kvmx8
- Add field Chipset in CreateRequest, CreateBlankRequest, MassCreateRequest, CreateBlankRequest in cloudapi/kvmx86 and cloudbroker/kvmx86
- Add function MassCreateAsync in cloudbroker/kvmx86
#### lb
- Change ExtNetID field from uint64 to int64 in CreateRequest, ItemLBList, RecordLB in cloudapi/lb, cloudbroker/lb
- Change ExtNetID, VINSID fields from required to optional in CreateRequest in cloudapi/lb
#### node
- Add field Force in EnableRequest in cloudbroker/node
- Add function EnableAsync in cloudbroker/node
#### rg
- Add endpoints removeDefNet in cloudapi/rg and cloudbroker/rg
- Add field ClearUniqPools in UpdateRequest in cloudbroker/rg and cloudapi/rg
- Add field UniqPools in UpdateRequest in cloudapi/rg
- Delete field IPCIDR in CreateRequest in cloudbroker/rg
#### sep
- Add endpoints SharedLockStart and SharedLockStop in cloudbroker/sep
#### user
- Add field GUID in models ItemAudit in cloudapi/user and cloudbroker/user
- Add fields MaxStatusCode and MinStatusCode in GetAuditRequest in cloudapi/user
#### vins
- Add field Computes in model ListVINS in cloudbroker/vins
- Add fields VNFDevId in ListRequest in cloudapi/vins and in ListDeletedRequest in cloudbroker/vins
- Add field MTU, LibvirtSettings in model ItemInterface in cloudapi/vins and cloudbroker/vins
- Add field BusNumber in model ItemInterface in cloudapi/vins and cloudbroker/vins
### Removed
#### common
- The reason field has been deleted everywhere
#### accounts
- The GetConsumption function was removed in cloudapi/account
#### grid
- The CheckVMs function was removed in cloudbroker/grid
- The CreateSystemSpace function was removed in cloudbroker/grid
#### image
- The Permanently field has been removed from DeleteCDROMImageRequest in cloudbroker/image
#### kvmppc
- The group has been completely deleted
#### sizes
- The group has been completely deleted
#### user
- The StatusCode field has been deleted from GetAuditRequest in cloudapi/user
### Bugfix
#### backup
- Fixed RestoreDisksFromBackupRequest in cloudbroker/backup

View File

@@ -186,7 +186,7 @@ APPENDIX: How to apply the Apache License to your work.
same "printed page" as the copyright notice for easier same "printed page" as the copyright notice for easier
identification within third-party archives. identification within third-party archives.
Copyright [yyyy] [name of copyright owner] Copyright 2022 Basis LTD
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

880
README.md

File diff suppressed because it is too large Load Diff

537
client.go
View File

@@ -4,11 +4,12 @@ import (
"bytes" "bytes"
"context" "context"
"crypto/tls" "crypto/tls"
"errors" "encoding/json"
"fmt" "fmt"
"io" "io"
"mime/multipart" "mime/multipart"
"net/http" "net/http"
"reflect"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
@@ -16,13 +17,13 @@ import (
"github.com/google/go-querystring/query" "github.com/google/go-querystring/query"
"repository.basistech.ru/BASIS/decort-golang-sdk/config" "repository.basistech.ru/BASIS/decort-golang-sdk/config"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/constants"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi" "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" "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 // DecortClient is HTTP-client for platform
type DecortClient struct { type DecortClient struct {
decortURL string decortURL string
client *http.Client client *http.Client
@@ -53,7 +54,7 @@ func New(cfg config.Config) *DecortClient {
}, },
}, },
}, },
cfg: cfg, cfg: trimConfig(&cfg),
expiryTime: expiryTime, expiryTime: expiryTime,
mutex: &sync.Mutex{}, mutex: &sync.Mutex{},
} }
@@ -71,15 +72,15 @@ func (dc *DecortClient) CloudBroker() *cloudbroker.CloudBroker {
// DecortApiCall method for sending requests to the platform // DecortApiCall method for sending requests to the platform
func (dc *DecortClient) DecortApiCall(ctx context.Context, method, url string, params interface{}) ([]byte, error) { func (dc *DecortClient) 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 body *bytes.Buffer
var ctype string var ctype string
if okCa { byteSlice, ok := params.([]byte)
body, ctype = createK8sCloudApi(k8sCaCreateReq) if ok {
} else if okCb { body = bytes.NewBuffer(byteSlice)
body, ctype = createK8sCloudBroker(k8sCbCreateReq) // ctype = "application/x-iso9660-image"
ctype = "application/octet-stream"
} else { } else {
values, err := query.Values(params) values, err := query.Values(params)
if err != nil { if err != nil {
@@ -88,323 +89,289 @@ func (dc *DecortClient) DecortApiCall(ctx context.Context, method, url string, p
body = bytes.NewBufferString(values.Encode()) body = bytes.NewBufferString(values.Encode())
} }
req, err := http.NewRequestWithContext(ctx, method, dc.decortURL+"/restmachine"+url, body) req, err := http.NewRequestWithContext(ctx, method, dc.decortURL+constants.RESTMACHINE+url, body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// get token
if err = dc.getToken(ctx); err != nil {
return nil, err
}
// perform request
respBytes, err := dc.do(req, ctype)
if err != nil {
return nil, err
}
return respBytes, err
}
// DecortApiCallMP method for sending requests to the platform
func (dc *DecortClient) DecortApiCallMP(ctx context.Context, method, url string, params interface{}) ([]byte, error) {
body, ctype, err := multiPartReq(params)
if err != nil {
return nil, err
}
req, err := http.NewRequestWithContext(ctx, method, dc.decortURL+constants.RESTMACHINE+url, body)
if err != nil {
return nil, err
}
// get token
if err = dc.getToken(ctx); err != nil { if err = dc.getToken(ctx); err != nil {
return nil, err return nil, err
} }
resp, err := dc.do(req, ctype) // perform request
if err != nil { respBytes, err := dc.do(req, ctype)
return nil, err
}
defer resp.Body.Close()
respBytes, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if resp.StatusCode != 200 { return respBytes, err
return nil, errors.New(string(respBytes))
}
return respBytes, nil
} }
func (dc *DecortClient) getToken(ctx context.Context) error { func (dc *DecortClient) getToken(ctx context.Context) error {
dc.mutex.Lock() dc.mutex.Lock()
defer dc.mutex.Unlock() defer dc.mutex.Unlock()
if dc.cfg.Token == "" || time.Now().After(dc.expiryTime) { // new token is not needed
body := fmt.Sprintf("grant_type=client_credentials&client_id=%s&client_secret=%s&response_type=id_token", dc.cfg.AppID, dc.cfg.AppSecret) if dc.cfg.Token != "" && !time.Now().After(dc.expiryTime) {
bodyReader := strings.NewReader(body) return nil
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)
} }
// set up request headers and body
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")
// request token
resp, err := dc.client.Do(req)
if err != nil || resp == nil {
return fmt.Errorf("cannot get token: %w", err)
}
defer resp.Body.Close()
var tokenBytes []byte
tokenBytes, err = io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("cannot get token: %w", err)
}
if resp.StatusCode != 200 {
return fmt.Errorf("cannot get token: %s", tokenBytes)
}
// save token in config
token := string(tokenBytes)
dc.cfg.Token = token
dc.expiryTime = time.Now().AddDate(0, 0, 1)
return nil return nil
} }
func (dc *DecortClient) do(req *http.Request, ctype string) (*http.Response, error) { // do method performs request and returns response as an array of bytes and nil error in case of response status code 200.
// In any other cases do returns nil response and error.
// Retries are implemented in case of connection reset errors.
func (dc *DecortClient) do(req *http.Request, ctype string) ([]byte, error) {
// set up request headers and body
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
if ctype != "" { if ctype != "" {
req.Header.Add("Content-Type", ctype) req.Header.Set("Content-Type", ctype)
} else {
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
} }
req.Header.Add("Authorization", "bearer "+dc.cfg.Token) req.Header.Add("Authorization", "bearer "+dc.cfg.Token)
req.Header.Set("Accept", "application/json") req.Header.Set("Accept", "application/json")
// var resp *http.Response buf, err := io.ReadAll(req.Body)
// var err error if err != nil {
buf, _ := io.ReadAll(req.Body) return nil, err
// req = req.Clone(req.Context())
// for i := uint64(0); i < dc.cfg.Retries; i++ {
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()
// }
// }
req.Body.Close()
req.Body = io.NopCloser(bytes.NewBuffer(buf))
resp, err := dc.client.Do(req)
if resp != nil {
defer resp.Body.Close()
}
// retries logic GOES HERE
// get http response
//var resp *http.Response
//for i := uint64(0); i < dc.cfg.Retries; i++ {
// req := req.Clone(req.Context())
// req.Body = io.NopCloser(bytes.NewBuffer(buf))
//
// if i > 0 {
// time.Sleep(5 * time.Second) // no time sleep for the first request
// }
//
// resp, err = dc.client.Do(req)
//
// // stop retries on success and close response body
// if resp != nil {
// defer resp.Body.Close()
// }
// if err == nil {
// break
// }
//
// // retries in case of connection errors with time sleep
// if isConnectionError(err) {
// continue
// }
//
// // return error in case of non-connection error
// return nil, err
//}
// handle http request errors
if err != nil {
return nil, err
}
if resp == nil {
return nil, fmt.Errorf("got empty response without error")
}
// handle successful request
respBytes, _ := io.ReadAll(resp.Body)
if resp.StatusCode == 200 {
return respBytes, nil
}
// handle errors with status code other than 200
err = fmt.Errorf("%s", respBytes)
return nil, fmt.Errorf("could not execute request: %w", err) return nil, fmt.Errorf("could not execute request: %w", err)
} }
func createK8sCloudApi(req k8s_ca.CreateRequest) (*bytes.Buffer, string) { // isConnectionError checks if given error falls within specific and associated connection errors
//func isConnectionError(err error) bool {
// if strings.Contains(err.Error(), "connection reset by peer") {
// return true
// }
// if errors.Is(err, io.EOF) {
// return true
// }
//
// return false
//}
// multiPartReq writes the request structure to the request body, and also returns string of the content-type
func multiPartReq(params interface{}) (*bytes.Buffer, string, error) {
reqBody := &bytes.Buffer{} reqBody := &bytes.Buffer{}
writer := multipart.NewWriter(reqBody) writer := multipart.NewWriter(reqBody)
if req.OidcCertificate != "" { values := reflect.ValueOf(params)
part, _ := writer.CreateFormFile("oidcCertificate", "ca.crt") types := values.Type()
_, _ = io.Copy(part, strings.NewReader(req.OidcCertificate)) defer writer.Close()
} for i := 0; i < values.NumField(); i++ {
if !values.Field(i).IsValid() {
continue
}
_ = writer.WriteField("name", req.Name) if values.Field(i).IsZero() {
_ = writer.WriteField("rgId", strconv.FormatUint(req.RGID, 10)) continue
_ = writer.WriteField("k8ciId", strconv.FormatUint(req.K8SCIID, 10)) }
_ = writer.WriteField("workerGroupName", req.WorkerGroupName)
_ = writer.WriteField("networkPlugin", req.NetworkPlugin)
if req.MasterSEPID != 0 { if file, ok := constants.FileName[types.Field(i).Name]; ok {
_ = writer.WriteField("masterSepId", strconv.FormatUint(req.MasterSEPID, 10)) part, err := writer.CreateFormFile(trimString(types.Field(i)), file)
} if err != nil {
if req.MasterSEPPool != "" { return &bytes.Buffer{}, "", err
_ = writer.WriteField("masterSepPool", req.MasterSEPPool) }
} _, err = io.Copy(part, strings.NewReader(valueToString(values.Field(i).Interface())))
if req.WorkerSEPID != 0 { if err != nil {
_ = writer.WriteField("workerSepId", strconv.FormatUint(req.WorkerSEPID, 10)) return &bytes.Buffer{}, "", err
} }
if req.WorkerSEPPool != "" { continue
_ = writer.WriteField("workerSepPool", req.WorkerSEPPool) }
}
if req.Labels != nil { if values.Field(i).Type().Kind() == reflect.Slice {
for _, v := range req.Labels { switch slice := values.Field(i).Interface().(type) {
_ = writer.WriteField("labels", v) case []string:
if validators.IsInSlice(trimString(types.Field(i)), constants.K8sValues) {
code, err := json.Marshal(slice)
if err != nil {
return &bytes.Buffer{}, "", err
}
err = writer.WriteField(trimString(types.Field(i)), string(code))
if err != nil {
return &bytes.Buffer{}, "", err
}
} else {
for _, val := range slice {
err := writer.WriteField(trimString(types.Field(i)), val)
if err != nil {
return &bytes.Buffer{}, "", err
}
}
}
case []uint:
for _, val := range slice {
err := writer.WriteField(trimString(types.Field(i)), strconv.FormatUint(uint64(val), 10))
if err != nil {
return &bytes.Buffer{}, "", err
}
}
case []uint64:
for _, val := range slice {
err := writer.WriteField(trimString(types.Field(i)), strconv.FormatUint(val, 10))
if err != nil {
return &bytes.Buffer{}, "", err
}
}
case []map[string]interface{}:
for _, val := range slice {
encodeStr, err := json.Marshal(val)
if err != nil {
return &bytes.Buffer{}, "", err
}
err = writer.WriteField(trimString(types.Field(i)), string(encodeStr))
if err != nil {
return &bytes.Buffer{}, "", err
}
}
default:
return &bytes.Buffer{}, "", fmt.Errorf("unsupported slice type:%T", slice)
}
continue
}
err := writer.WriteField(trimString(types.Field(i)), valueToString(values.Field(i).Interface()))
if err != nil {
return &bytes.Buffer{}, "", err
} }
} }
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() ct := writer.FormDataContentType()
writer.Close() return reqBody, ct, nil
return reqBody, ct
} }
func createK8sCloudBroker(req k8s_cb.CreateRequest) (*bytes.Buffer, string) { func valueToString(a any) string {
reqBody := &bytes.Buffer{} switch str := a.(type) {
writer := multipart.NewWriter(reqBody) case string:
if req.OidcCertificate != "" { return str
part, _ := writer.CreateFormFile("oidcCertificate", "ca.crt") case uint:
_, _ = io.Copy(part, strings.NewReader(req.OidcCertificate)) return strconv.FormatUint(uint64(str), 10)
case uint64:
return strconv.FormatUint(str, 10)
case bool:
return strconv.FormatBool(str)
default:
return ""
} }
}
_ = writer.WriteField("name", req.Name)
_ = writer.WriteField("rgId", strconv.FormatUint(req.RGID, 10)) func trimString(el reflect.StructField) string {
_ = writer.WriteField("k8ciId", strconv.FormatUint(req.K8CIID, 10)) return strings.TrimSuffix(el.Tag.Get("url"), ",omitempty")
_ = writer.WriteField("workerGroupName", req.WorkerGroupName) }
_ = writer.WriteField("networkPlugin", req.NetworkPlugin)
func trimConfig(cfg *config.Config) config.Config {
if req.MasterSEPID != 0 { cfg.SSOURL = strings.TrimSuffix(cfg.SSOURL, "/")
_ = writer.WriteField("masterSepId", strconv.FormatUint(req.MasterSEPID, 10)) cfg.DecortURL = strings.TrimSuffix(cfg.DecortURL, "/")
} return *cfg
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
} }

395
client_bvs.go Normal file
View File

@@ -0,0 +1,395 @@
package decortsdk
import (
"bytes"
"context"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"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/constants"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker"
)
// BVSDecortClient is HTTP-client for platform
type BVSDecortClient struct {
client *http.Client
cfg config.BVSConfig
mutex *sync.Mutex
decortURL string
}
type tokenJSON struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
RefreshToken string `json:"refresh_token"`
ExpiresIn uint64 `json:"expires_in"`
}
// Сlient builder
func NewBVS(cfg config.BVSConfig) *BVSDecortClient {
if cfg.Retries == 0 {
cfg.Retries = 5
}
if cfg.TimeToRefresh == 0 {
cfg.TimeToRefresh = 1
}
return &BVSDecortClient{
decortURL: cfg.DecortURL,
client: &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
//nolint:gosec
InsecureSkipVerify: cfg.SSLSkipVerify,
},
},
},
cfg: trimBVSConfig(&cfg),
mutex: &sync.Mutex{},
}
}
// 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) {
var body *bytes.Buffer
var ctype string
byteSlice, ok := params.([]byte)
if ok {
body = bytes.NewBuffer(byteSlice)
// ctype = "application/x-iso9660-image"
ctype = "application/octet-stream"
} 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+constants.RESTMACHINE+url, body)
if err != nil {
return nil, err
}
// get token
if bdc.cfg.Token.AccessToken == "" {
if _, err = bdc.GetToken(ctx); err != nil {
return nil, err
}
}
// refresh token
if bdc.cfg.Token.RefreshToken != "" && bdc.cfg.Token.Expiry.Add(-time.Duration(bdc.cfg.TimeToRefresh)*time.Minute).Before(time.Now()) {
if _, err := bdc.RefreshToken(ctx); err != nil {
if _, err = bdc.GetToken(ctx); err != nil {
return nil, err
}
}
}
// perform request
reqCopy := req.Clone(ctx)
respBytes, err := bdc.do(req, ctype)
if err == nil {
return respBytes, nil
}
// get token and retry in case of access denied
if err.Error() == "access is denied" {
_, err = bdc.GetToken(ctx)
if err != nil {
return nil, err
}
respBytes, err = bdc.do(reqCopy, "")
if err != nil {
return nil, err
}
}
return respBytes, err
}
func (bdc *BVSDecortClient) DecortApiCallMP(ctx context.Context, method, url string, params interface{}) ([]byte, error) {
body, ctype, err := multiPartReq(params)
if err != nil {
return nil, err
}
req, err := http.NewRequestWithContext(ctx, method, bdc.decortURL+constants.RESTMACHINE+url, body)
if err != nil {
return nil, err
}
// get token
if bdc.cfg.Token.AccessToken == "" {
if _, err = bdc.GetToken(ctx); err != nil {
return nil, err
}
}
// refresh token
if bdc.cfg.Token.RefreshToken != "" && bdc.cfg.Token.Expiry.Add(-time.Duration(bdc.cfg.TimeToRefresh)*time.Minute).Before(time.Now()) {
if _, err := bdc.RefreshToken(ctx); err != nil {
if _, err = bdc.GetToken(ctx); err != nil {
return nil, err
}
}
}
// perform request
reqCopy := req.Clone(ctx)
respBytes, err := bdc.do(req, ctype)
if err == nil {
return respBytes, nil
}
// get token and retry in case of access denied
if err.Error() == "access is denied" {
_, err = bdc.GetToken(ctx)
if err != nil {
return nil, err
}
respBytes, err = bdc.do(reqCopy, ctype)
if err != nil {
return nil, err
}
}
return respBytes, err
}
// GetToken allows you to get a token and returns the token structure. When specifying the PathCfg variable,
// the token and configuration will be written to a file.
// When specifying the PathToken variable, the token will be written to a file.
func (bdc *BVSDecortClient) GetToken(ctx context.Context) (config.Token, error) {
bdc.mutex.Lock()
defer bdc.mutex.Unlock()
// set up request headers and body
body := fmt.Sprintf("grant_type=password&client_id=%s&client_secret=%s&username=%s&password=%s&response_type=token&scope=openid", bdc.cfg.AppID, bdc.cfg.AppSecret, bdc.cfg.Username, bdc.cfg.Password)
bodyReader := strings.NewReader(body)
bdc.cfg.SSOURL = strings.TrimSuffix(bdc.cfg.SSOURL, "/")
req, _ := http.NewRequestWithContext(ctx, "POST", bdc.cfg.SSOURL+"/realms/"+bdc.cfg.Domain+"/protocol/openid-connect/token", bodyReader)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
// request token
resp, err := bdc.client.Do(req)
if err != nil || resp == nil {
return config.Token{}, fmt.Errorf("cannot get token: %w", err)
}
defer resp.Body.Close()
var tokenBytes []byte
tokenBytes, err = io.ReadAll(resp.Body)
if err != nil {
return config.Token{}, fmt.Errorf("cannot get token: %w", err)
}
if resp.StatusCode != 200 {
return config.Token{}, fmt.Errorf("cannot get token: %s", tokenBytes)
}
// save token in config
var tj tokenJSON
if err = json.Unmarshal(tokenBytes, &tj); err != nil {
return config.Token{}, fmt.Errorf("cannot unmarshal token: %w", err)
}
bdc.cfg.Token = config.Token{
AccessToken: tj.AccessToken,
TokenType: tj.TokenType,
RefreshToken: tj.RefreshToken,
Expiry: tj.expiry(),
}
if bdc.cfg.PathCfg != "" {
ser, _ := bdc.cfg.Serialize("", " ")
_ = ser.WriteToFile(bdc.cfg.PathCfg)
}
if bdc.cfg.PathToken != "" {
ser, _ := bdc.cfg.Token.Serialize("", " ")
_ = ser.WriteToFile(bdc.cfg.PathToken)
}
return bdc.cfg.Token, nil
}
// RefreshToken allows you to refresh a token and returns the token structure. When specifying the PathCfg variable,
// the token and configuration will be written to a file.
// When specifying the PathToken variable, the token will be written to a file
func (bdc *BVSDecortClient) RefreshToken(ctx context.Context) (config.Token, error) {
bdc.mutex.Lock()
defer bdc.mutex.Unlock()
// set up request headers and body
body := fmt.Sprintf("grant_type=refresh_token&client_id=%s&client_secret=%s&refresh_token=%s&scope=openid", bdc.cfg.AppID, bdc.cfg.AppSecret, bdc.cfg.Token.RefreshToken)
bodyReader := strings.NewReader(body)
bdc.cfg.SSOURL = strings.TrimSuffix(bdc.cfg.SSOURL, "/")
req, _ := http.NewRequestWithContext(ctx, "POST", bdc.cfg.SSOURL+"/realms/"+bdc.cfg.Domain+"/protocol/openid-connect/token", bodyReader)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
// refresh token
resp, err := bdc.client.Do(req)
if err != nil || resp == nil {
return config.Token{}, fmt.Errorf("cannot refresh token: %w", err)
}
defer resp.Body.Close()
var tokenBytes []byte
tokenBytes, err = io.ReadAll(resp.Body)
if err != nil {
return config.Token{}, fmt.Errorf("cannot refresh token: %w", err)
}
if resp.StatusCode != 200 {
return config.Token{}, fmt.Errorf("cannot refresh token: %s", tokenBytes)
}
// save token in config
var tj tokenJSON
if err = json.Unmarshal(tokenBytes, &tj); err != nil {
return config.Token{}, fmt.Errorf("cannot unmarshal after refresh token: %w", err)
}
bdc.cfg.Token = config.Token{
AccessToken: tj.AccessToken,
TokenType: tj.TokenType,
RefreshToken: tj.RefreshToken,
Expiry: tj.expiry(),
}
if bdc.cfg.PathCfg != "" {
ser, _ := bdc.cfg.Serialize("", " ")
_ = ser.WriteToFile(bdc.cfg.PathCfg)
}
if bdc.cfg.PathToken != "" {
ser, _ := bdc.cfg.Token.Serialize("", " ")
_ = ser.WriteToFile(bdc.cfg.PathToken)
}
return bdc.cfg.Token, nil
}
func (e *tokenJSON) expiry() (t time.Time) {
if v := e.ExpiresIn; v != 0 {
return time.Now().Add(time.Duration(v) * time.Second)
}
return
}
// do method performs request and returns response as an array of bytes and nil error in case of response status code 200.
// In any other cases do returns nil response and error.
// Retries are implemented in case of connection reset errors.
func (bdc *BVSDecortClient) do(req *http.Request, ctype string) ([]byte, error) {
// set up request headers and body
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
if ctype != "" {
req.Header.Set("Content-Type", ctype)
}
req.Header.Add("Authorization", "bearer "+bdc.cfg.Token.AccessToken)
req.Header.Set("Accept", "application/json")
buf, err := io.ReadAll(req.Body)
if err != nil {
return nil, err
}
req.Body.Close()
req.Body = io.NopCloser(bytes.NewBuffer(buf))
resp, err := bdc.client.Do(req)
if resp != nil {
defer resp.Body.Close()
}
// retries logic GOES HERE
// get http response
//var resp *http.Response
//for i := uint64(0); i < bdc.cfg.Retries; i++ {
// req := req.Clone(req.Context())
// req.Body = io.NopCloser(bytes.NewBuffer(buf))
//
// if i > 0 {
// time.Sleep(5 * time.Second) // no time sleep for the first request
// }
//
// resp, err = bdc.client.Do(req)
//
// // stop retries on success and close response body
// if resp != nil {
// defer resp.Body.Close()
// }
// if err == nil {
// break
// }
//
// // retries in case of connection errors with time sleep
// if isConnectionError(err) {
// continue
// }
//
// // return error in case of non-connection error
// return nil, err
//}
// handle http request errors
if err != nil {
return nil, err
}
if resp == nil {
return nil, fmt.Errorf("got empty response without error")
}
var respBytes []byte
respBytes, err = io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
// handle access denied and successful request
if resp.StatusCode == 401 {
return respBytes, errors.New("access is denied")
}
if resp.StatusCode == 200 {
return respBytes, nil
}
// handle errors with other status codes
err = fmt.Errorf("%s", respBytes)
return nil, fmt.Errorf("could not execute request: %w", err)
}
func trimBVSConfig(cfg *config.BVSConfig) config.BVSConfig {
cfg.SSOURL = strings.TrimSuffix(cfg.SSOURL, "/")
cfg.DecortURL = strings.TrimSuffix(cfg.DecortURL, "/")
return *cfg
}

216
config/config_bvs.go Normal file
View File

@@ -0,0 +1,216 @@
package config
import (
"encoding/json"
"os"
"time"
"gopkg.in/yaml.v3"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/serialization"
"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: "dynamix"
Domain string `json:"domain" yaml:"domain" validate:"required"`
// 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 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"`
// The path of the configuration file entry
// Required: false
PathCfg string `json:"path_cfg" yaml:"path_cfg"`
// The path of the token file entry
// Required: false
PathToken string `json:"path_token" yaml:"path_token"`
// The number of minutes before the expiration of the token, a refresh will be made
// Required: false
TimeToRefresh int64 `json:"timeToRefresh" yaml:"timeToRefresh"`
}
type Token struct {
// AccessToken is the token that authorizes and authenticates
// the requests.
// Required: false
AccessToken string `json:"access_token" yaml:"access_token"`
// TokenType is the type of token.
// The Type method returns either this or "Bearer", the default.
// Required: false
TokenType string `json:"token_type" yaml:"token_type"`
// RefreshToken is a token that's used by the application
// (as opposed to the user) to refresh the access token
// if it expires.
// Required: false
RefreshToken string `json:"refresh_token" yaml:"refresh_token"`
// Expiry is the optional expiration time of the access token.
// Required: false
Expiry time.Time `json:"expiry" yaml:"expiry"`
}
// 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
}
// ParseConfigJSON parses Token from specified JSON-formatted file.
func ParseTokenBVSJSON(path string) (Token, error) {
file, err := os.ReadFile(path)
if err != nil {
return Token{}, err
}
var token Token
err = json.Unmarshal(file, &token)
if err != nil {
return Token{}, err
}
err = validators.ValidateConfig(token)
if err != nil {
return Token{}, validators.ValidationErrors(validators.GetErrors(err))
}
return token, nil
}
// ParseTokenBVSYAML parses Token from specified YAML-formatted file.
func ParseTokenBVSYAML(path string) (Token, error) {
file, err := os.ReadFile(path)
if err != nil {
return Token{}, err
}
var token Token
err = yaml.Unmarshal(file, &token)
if err != nil {
return Token{}, err
}
err = validators.ValidateConfig(token)
if err != nil {
return Token{}, validators.ValidationErrors(validators.GetErrors(err))
}
return token, 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
}
func (t Token) Serialize(params ...string) (serialization.Serialized, error) {
if len(params) > 1 {
prefix := params[0]
indent := params[1]
return json.MarshalIndent(t, prefix, indent)
}
return json.Marshal(t)
}
func (c BVSConfig) Serialize(params ...string) (serialization.Serialized, error) {
if len(params) > 1 {
prefix := params[0]
indent := params[1]
return json.MarshalIndent(c, prefix, indent)
}
return json.Marshal(c)
}

View File

@@ -22,6 +22,8 @@ func (d *Duration) UnmarshalYAML(unmarshal func(interface{}) error) error {
} }
*d = Duration(tmp) *d = Duration(tmp)
return nil return nil
case float64:
return nil
default: default:
return fmt.Errorf("invalid duration %v", value) return fmt.Errorf("invalid duration %v", value)
} }
@@ -40,6 +42,8 @@ func (d *Duration) UnmarshalJSON(b []byte) error {
} }
*d = Duration(tmp) *d = Duration(tmp)
return nil return nil
case float64:
return nil
default: default:
return fmt.Errorf("invalid duration %v", value) return fmt.Errorf("invalid duration %v", value)
} }

View File

@@ -0,0 +1,8 @@
package config
// UniversalConfig combines configurations for different types of clients
type UniversalConfig struct {
Decs3oConfig *Config `json:"decs3oConfig,omitempty" yaml:"decs3oConfig,omitempty"`
BVSConfig *BVSConfig `json:"bvsConfig,omitempty" yaml:"bvsConfig,omitempty"`
LegacyConfig *LegacyConfig `json:"legacyConfig,omitempty" yaml:"legacyConfig,omitempty"`
}

8
go.mod
View File

@@ -5,15 +5,17 @@ go 1.20
require ( require (
github.com/go-playground/validator/v10 v10.11.2 github.com/go-playground/validator/v10 v10.11.2
github.com/google/go-querystring v1.1.0 github.com/google/go-querystring v1.1.0
github.com/joho/godotenv v1.5.1
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
require ( require (
github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/kr/text v0.2.0 // indirect github.com/kr/text v0.2.0 // indirect
github.com/leodido/go-urn v1.2.1 // indirect github.com/leodido/go-urn v1.2.1 // indirect
golang.org/x/crypto v0.5.0 // indirect golang.org/x/crypto v0.15.0 // indirect
golang.org/x/sys v0.4.0 // indirect golang.org/x/sys v0.14.0 // indirect
golang.org/x/text v0.6.0 // indirect golang.org/x/text v0.14.0 // indirect
) )

17
go.sum
View File

@@ -8,10 +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/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 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU=
github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s= 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/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= 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/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
@@ -23,12 +26,12 @@ 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/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.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 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.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 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/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=

View File

@@ -6,4 +6,7 @@ import "context"
type Caller interface { type Caller interface {
// DecortApiCall method for sending requests to the platform // DecortApiCall method for sending requests to the platform
DecortApiCall(ctx context.Context, method, url string, params interface{}) ([]byte, error) DecortApiCall(ctx context.Context, method, url string, params interface{}) ([]byte, error)
// DecortApiCallMP method for sending requests to the platform
DecortApiCallMP(ctx context.Context, method, url string, params interface{}) ([]byte, error)
} }

7
interfaces/request.go Normal file
View File

@@ -0,0 +1,7 @@
package interfaces
// Interface to valiate RAM values
type RequestWithRAM interface {
// GetRAM returns RAM values
GetRAM() map[string]uint64
}

View File

@@ -0,0 +1,14 @@
package constants
const (
RESTMACHINE = "/restmachine"
// RAM_DIVISIBILITY sets divisibility of RAM value
RAM_DIVISIBILITY uint64 = 128
)
var FileName = map[string]string{
"OidcCertificate": "ca.crt",
}
var K8sValues = []string{"labels", "taints", "annotations, additionalSANs"}

View File

@@ -1,31 +1,37 @@
package validators package validators
import ( import (
"errors"
"fmt"
"net/url"
"reflect"
"regexp" "regexp"
"strings" "strings"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
"repository.basistech.ru/BASIS/decort-golang-sdk/interfaces"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/multierror"
) )
// computeDriverValidator is used to validate Driver field in kvmx86/kvmppc create. // computeDriverValidator is used to validate Driver field in kvmx86 create.
func computeDriverValidator(fe validator.FieldLevel) bool { func computeDriverValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String() fieldValue := fe.Field().String()
return StringInSlice(fieldValue, computeDriverValues) return IsInSlice(fieldValue, computeDriverValues)
} }
// protoValidator is used to validate Proto fields. // protoValidator is used to validate Proto fields.
func protoValidator(fe validator.FieldLevel) bool { func protoValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String() fieldValue := fe.Field().String()
return StringInSlice(fieldValue, protoValues) return IsInSlice(fieldValue, protoValues)
} }
// accessTypeValidator is used to validate AccessType fields. // accessTypeValidator is used to validate AccessType fields.
func accessTypeValidator(fe validator.FieldLevel) bool { func accessTypeValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String() fieldValue := fe.Field().String()
return StringInSlice(fieldValue, accessTypeValues) return IsInSlice(fieldValue, accessTypeValues)
} }
// resTypesValidator is used to validate ResTypes fields. // resTypesValidator is used to validate ResTypes fields.
@@ -36,7 +42,7 @@ func resTypesValidator(fe validator.FieldLevel) bool {
} }
for _, value := range fieldSlice { for _, value := range fieldSlice {
if !StringInSlice(value, resTypesValues) { if !IsInSlice(value, resTypesValues) {
return false return false
} }
} }
@@ -48,56 +54,63 @@ func resTypesValidator(fe validator.FieldLevel) bool {
func driverValidator(fe validator.FieldLevel) bool { func driverValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String() fieldValue := fe.Field().String()
return StringInSlice(fieldValue, driverValues) return IsInSlice(fieldValue, driverValues)
} }
// accountCUTypeValidator is used to validate CUType field. // accountCUTypeValidator is used to validate CUType field.
func accountCUTypeValidator(fe validator.FieldLevel) bool { func accountCUTypeValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String() fieldValue := fe.Field().String()
return StringInSlice(fieldValue, accountCUTypeValues) return IsInSlice(fieldValue, accountCUTypeValues)
} }
// bserviceModeValidator is used to validate Mode field. // bserviceModeValidator is used to validate Mode field.
func bserviceModeValidator(fe validator.FieldLevel) bool { func bserviceModeValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String() fieldValue := fe.Field().String()
return StringInSlice(fieldValue, bserviceModeValues) return IsInSlice(fieldValue, bserviceModeValues)
} }
// computeTopologyValidator is used to validate Topology field. // computeTopologyValidator is used to validate Topology field.
func computeTopologyValidator(fe validator.FieldLevel) bool { func computeTopologyValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String() fieldValue := fe.Field().String()
return StringInSlice(fieldValue, computeTopologyValues) return IsInSlice(fieldValue, computeTopologyValues)
} }
// computePolicyValidator is used to validate Policy field. // computePolicyValidator is used to validate Policy field.
func computePolicyValidator(fe validator.FieldLevel) bool { func computePolicyValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String() fieldValue := fe.Field().String()
return StringInSlice(fieldValue, computePolicyValues) return IsInSlice(fieldValue, computePolicyValues)
} }
// computeModeValidator is used to validate Mode field. // computeModeValidator is used to validate Mode field.
func computeModeValidator(fe validator.FieldLevel) bool { func computeModeValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String() fieldValue := fe.Field().String()
return StringInSlice(fieldValue, computeModeValues) return IsInSlice(fieldValue, computeModeValues)
} }
// computeDiskTypeValidator is used to validate DiskType field. // computeDiskTypeValidator is used to validate DiskType field.
func computeDiskTypeValidator(fe validator.FieldLevel) bool { func computeDiskTypeValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String() fieldValue := fe.Field().String()
return StringInSlice(fieldValue, computeDiskTypeValues) return IsInSlice(fieldValue, computeDiskTypeValues)
} }
// computeNetTypeValidator is used to validate NetType field. // computeNetTypeValidator is used to validate NetType field.
func computeNetTypeValidator(fe validator.FieldLevel) bool { func computeNetTypeValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String() fieldValue := fe.Field().String()
return StringInSlice(fieldValue, computeNetTypeValues) return IsInSlice(fieldValue, computeNetTypeValues)
}
// computex86NetTypeValidator is used to validate NetType field.
func computex86NetTypeValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
return IsInSlice(fieldValue, computex86NetTypeValues)
} }
// computeOrderValidator is used to validate Order field. // computeOrderValidator is used to validate Order field.
@@ -108,7 +121,7 @@ func computeOrderValidator(fe validator.FieldLevel) bool {
} }
for _, value := range fieldSlice { for _, value := range fieldSlice {
if !StringInSlice(value, computeOrderValues) { if !IsInSlice(value, computeOrderValues) {
return false return false
} }
} }
@@ -120,70 +133,70 @@ func computeOrderValidator(fe validator.FieldLevel) bool {
func computeDataDisksValidator(fe validator.FieldLevel) bool { func computeDataDisksValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String() fieldValue := fe.Field().String()
return StringInSlice(fieldValue, computeDataDisksValues) return IsInSlice(fieldValue, computeDataDisksValues)
} }
// diskTypeValidator is used to validate Type field. // diskTypeValidator is used to validate Type field.
func diskTypeValidator(fe validator.FieldLevel) bool { func diskTypeValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String() fieldValue := fe.Field().String()
return StringInSlice(fieldValue, diskTypeValues) return IsInSlice(fieldValue, diskTypeValues)
} }
// flipgroupClientTypeValidator is used to validate ClientType field. // flipgroupClientTypeValidator is used to validate ClientType field.
func flipgroupClientTypeValidator(fe validator.FieldLevel) bool { func flipgroupClientTypeValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String() fieldValue := fe.Field().String()
return StringInSlice(fieldValue, flipgroupClientTypeValues) return IsInSlice(fieldValue, flipgroupClientTypeValues)
} }
// kvmNetTypeValidator is used to validate NetType field. // kvmNetTypeValidator is used to validate NetType field.
func kvmNetTypeValidator(fe validator.FieldLevel) bool { func kvmNetTypeValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String() fieldValue := fe.Field().String()
return StringInSlice(fieldValue, kvmNetTypeValues) return IsInSlice(fieldValue, kvmNetTypeValues)
} }
// lbAlgorithmValidator is used to validate Algorithm field. // lbAlgorithmValidator is used to validate Algorithm field.
func lbAlgorithmValidator(fe validator.FieldLevel) bool { func lbAlgorithmValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String() fieldValue := fe.Field().String()
return StringInSlice(fieldValue, lbAlgorithmValues) return IsInSlice(fieldValue, lbAlgorithmValues)
} }
// rgDefNetValidator is used to validate DefNet field. // rgDefNetValidator is used to validate DefNet field.
func rgDefNetValidator(fe validator.FieldLevel) bool { func rgDefNetValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String() fieldValue := fe.Field().String()
return StringInSlice(fieldValue, rgDefNetValues) return IsInSlice(fieldValue, rgDefNetValues)
} }
// rgNetTypeValidator is used to validate NetType field. // rgNetTypeValidator is used to validate NetType field.
func rgNetTypeValidator(fe validator.FieldLevel) bool { func rgNetTypeValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String() fieldValue := fe.Field().String()
return StringInSlice(fieldValue, rgNetTypeValues) return IsInSlice(fieldValue, rgNetTypeValues)
} }
// vinsTypeValidator is used to validate Type field. // vinsTypeValidator is used to validate Type field.
func vinsTypeValidator(fe validator.FieldLevel) bool { func vinsTypeValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String() fieldValue := fe.Field().String()
return StringInSlice(fieldValue, vinsTypeValues) return IsInSlice(fieldValue, vinsTypeValues)
} }
// imageBootTypeValidator is used to validate BootType field. // imageBootTypeValidator is used to validate BootType field.
func imageBootTypeValidator(fe validator.FieldLevel) bool { func imageBootTypeValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String() fieldValue := fe.Field().String()
return StringInSlice(fieldValue, imageBootTypeValues) return IsInSlice(fieldValue, imageBootTypeValues)
} }
// imageTypeValidator is used to validate ImageType field. // imageTypeValidator is used to validate ImageType field.
func imageTypeValidator(fe validator.FieldLevel) bool { func imageTypeValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String() fieldValue := fe.Field().String()
return StringInSlice(fieldValue, imageTypeValues) return IsInSlice(fieldValue, imageTypeValues)
} }
// imageDriversValidator is used to validate Drivers field. // imageDriversValidator is used to validate Drivers field.
@@ -194,7 +207,7 @@ func imageDriversValidator(fe validator.FieldLevel) bool {
} }
for _, item := range fieldSlice { for _, item := range fieldSlice {
if !StringInSlice(item, imageDriversValues) { if !IsInSlice(item, imageDriversValues) {
return false return false
} }
} }
@@ -206,14 +219,14 @@ func imageDriversValidator(fe validator.FieldLevel) bool {
func imageArchitectureValidator(fe validator.FieldLevel) bool { func imageArchitectureValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String() fieldValue := fe.Field().String()
return StringInSlice(fieldValue, imageArchitectureValues) return IsInSlice(fieldValue, imageArchitectureValues)
} }
// sepFieldTypeValidator is used to validate FieldType field. // sepFieldTypeValidator is used to validate FieldType field.
func sepFieldTypeValidator(fe validator.FieldLevel) bool { func sepFieldTypeValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String() fieldValue := fe.Field().String()
return StringInSlice(fieldValue, sepFieldTypeValues) return IsInSlice(fieldValue, sepFieldTypeValues)
} }
// hwPathValidator is used to validate HWPath field. // hwPathValidator is used to validate HWPath field.
@@ -230,7 +243,7 @@ func networkPluginValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String() fieldValue := fe.Field().String()
fieldValue = strings.ToLower(fieldValue) fieldValue = strings.ToLower(fieldValue)
return StringInSlice(fieldValue, networkPluginValues) return IsInSlice(fieldValue, networkPluginValues)
} }
// networkPluginsValidator is used to validate NetworkPlugins field // networkPluginsValidator is used to validate NetworkPlugins field
@@ -243,7 +256,7 @@ func networkPluginsValidator(fe validator.FieldLevel) bool {
for _, item := range fieldSlice { for _, item := range fieldSlice {
item = strings.ToLower(item) item = strings.ToLower(item)
if !StringInSlice(item, networkPluginValues) { if !IsInSlice(item, networkPluginValues) {
return false return false
} }
} }
@@ -255,14 +268,35 @@ func interfaceStateValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String() fieldValue := fe.Field().String()
fieldValue = strings.ToLower(fieldValue) fieldValue = strings.ToLower(fieldValue)
return StringInSlice(fieldValue, interfaceStateValues) return IsInSlice(fieldValue, interfaceStateValues)
}
func interfaceTXModelValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
fieldValue = strings.ToLower(fieldValue)
return IsInSlice(fieldValue, txModelValues)
}
func interfaceIOEventFDValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
fieldValue = strings.ToLower(fieldValue)
return IsInSlice(fieldValue, ioEventFDValues)
}
func interfaceEventIDxValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
fieldValue = strings.ToLower(fieldValue)
return IsInSlice(fieldValue, eventIDxValues)
} }
func strictLooseValidator(fe validator.FieldLevel) bool { func strictLooseValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String() fieldValue := fe.Field().String()
fieldValue = strings.ToLower(fieldValue) fieldValue = strings.ToLower(fieldValue)
return StringInSlice(fieldValue, strictLooseValues) return IsInSlice(fieldValue, strictLooseValues)
} }
// name workerGroup must be more 3 symbol // name workerGroup must be more 3 symbol
@@ -272,3 +306,90 @@ func workerGroupNameValidator(fe validator.FieldLevel) bool {
return len(fieldValue) >= 3 return len(fieldValue) >= 3
} }
func sortByValidator(fe validator.FieldLevel) bool {
sortByRegexp := regexp.MustCompile(`^[+-][a-zA-Z_]+`)
return sortByRegexp.MatchString(fe.Field().String())
}
func actionValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
return IsInSlice(fieldValue, actionValues)
}
func vmActionValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
return IsInSlice(fieldValue, vmActionValues)
}
func mtuValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().Uint()
return fieldValue >= uint64(mtuMin) && fieldValue <= uint64(mtuMax)
}
func computeFeaturesValidator(fe validator.FieldLevel) bool {
field := fe.Field()
slice, ok := field.Interface().([]string)
if !ok {
return false
}
return IsSubSlice(slice, computeFeaturesValues)
}
func networkInterfaceNamingValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
return IsInSlice(fieldValue, networkInterfaceNamingValues)
}
func numaAffinityValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
return IsInSlice(fieldValue, numaAffinityValues)
}
// kvmx86NetTypeValidator is used to validate NetType field for x86 compute.
func kvmx86NetTypeValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
return IsInSlice(fieldValue, kvmx86NetTypeValues)
}
func isBoolTypeValidator(fe validator.FieldLevel) bool {
return fe.Field().CanConvert(reflect.TypeOf(true))
}
func urlValidartor(fl validator.FieldLevel) bool {
fieldValues := fl.Field().String()
_, err := url.ParseRequestURI(fieldValues)
return err == nil
}
// ValidateRAM checks if request contains RAM value that is positive integer divisible by divisibility passed.
// It is recommended to pass constants.RAM_DIVISIBILITY as divisility arguement
func ValidateRAM(r interfaces.RequestWithRAM, divisibility uint64) error {
if divisibility == 0 {
return errors.New("second argument of ValidateRAM should be greater than 0")
}
mapRAM := r.GetRAM()
errs := make([]error, 0, len(mapRAM))
for k, v := range mapRAM {
if v%divisibility != 0 {
errs = append(errs, fmt.Errorf("expected value of %s: \"%d\" should be divisible by %d", k, v, divisibility))
}
}
return multierror.Join(errs...)
}

View File

@@ -34,7 +34,7 @@ func GetErrors(err error) validator.ValidationErrors {
return err.(validator.ValidationErrors) return err.(validator.ValidationErrors)
} }
func StringInSlice(str string, target []string) bool { func IsInSlice(str string, target []string) bool {
for _, v := range target { for _, v := range target {
if v == str { if v == str {
return true return true
@@ -42,3 +42,12 @@ func StringInSlice(str string, target []string) bool {
} }
return false return false
} }
func IsSubSlice(source []string, target []string) bool {
for _, s := range source {
if !IsInSlice(s, target) {
return false
}
}
return true
}

View File

@@ -25,6 +25,8 @@ func errorMessage(fe validator.FieldError) string {
return fmt.Sprintf("%s %s: unexpected URL format", prefix, fe.Field()) return fmt.Sprintf("%s %s: unexpected URL format", prefix, fe.Field())
case "email": case "email":
return fmt.Sprintf("%s %s: unexpected E-Mail format", prefix, fe.Field()) return fmt.Sprintf("%s %s: unexpected E-Mail format", prefix, fe.Field())
case "isBool":
return fmt.Sprintf("%s %s: must be bool type", prefix, fe.Field())
case "driver": case "driver":
return fmt.Sprintf("%s %s must be one of the following: %s", return fmt.Sprintf("%s %s must be one of the following: %s",
@@ -89,6 +91,15 @@ func errorMessage(fe validator.FieldError) string {
fe.Field(), fe.Field(),
joinValues(computeDiskTypeValues)) joinValues(computeDiskTypeValues))
case "mtu":
return fmt.Sprint(prefix, fe.Field(), "must be ", mtuMin, "-", mtuMax)
case "computex86NetType":
return fmt.Sprintf("%s %s must be one of the following: %s",
prefix,
fe.Field(),
joinValues(computex86NetTypeValues))
case "computeNetType": case "computeNetType":
return fmt.Sprintf("%s %s must be one of the following: %s", return fmt.Sprintf("%s %s must be one of the following: %s",
prefix, prefix,
@@ -133,7 +144,7 @@ func errorMessage(fe validator.FieldError) string {
prefix, prefix,
fe.Field()) fe.Field())
// KVM_X86/KVM_PPC Validators // KVM_X86 Validators
case "kvmNetType": case "kvmNetType":
return fmt.Sprintf("%s %s must be one of the following: %s", return fmt.Sprintf("%s %s must be one of the following: %s",
prefix, prefix,
@@ -229,6 +240,65 @@ func errorMessage(fe validator.FieldError) string {
prefix, prefix,
fe.Field(), fe.Field(),
joinValues(interfaceStateValues)) joinValues(interfaceStateValues))
case "interfaceTXModel":
return fmt.Sprintf("%s %s must be one of the following: %s",
prefix,
fe.Field(),
joinValues(txModelValues))
case "interfaceIOEventFD":
return fmt.Sprintf("%s %s must be one of the following: %s",
prefix,
fe.Field(),
joinValues(ioEventFDValues))
case "interfaceEventIDx":
return fmt.Sprintf("%s %s must be one of the following: %s",
prefix,
fe.Field(),
joinValues(eventIDxValues))
case "sortBy":
return fmt.Sprintf("%s %s must be in format +|-(field)",
prefix,
fe.Field())
case "action":
return fmt.Sprintf("%s %s must be one of the following: %s",
prefix,
fe.Field(),
joinValues(actionValues))
case "vmaction":
return fmt.Sprintf("%s %s must be one of the following: %s",
prefix,
fe.Field(),
joinValues(vmActionValues))
case "computeFeatures":
return fmt.Sprintf("%s %s must be one of the following: %s",
prefix,
fe.Field(),
joinValues(computeFeaturesValues))
case "networkInterfaceNaming":
return fmt.Sprintf("%s %s must be one of the following: %s",
prefix,
fe.Field(),
joinValues(networkInterfaceNamingValues))
case "numaAffinity":
return fmt.Sprintf("%s %s must be one of the following: %s",
prefix,
fe.Field(),
joinValues(numaAffinityValues))
case "kvmx86NetType":
return fmt.Sprintf("%s %s must be one of the following: %s",
prefix,
fe.Field(),
joinValues(kvmx86NetTypeValues))
} }
return fe.Error() return fe.Error()

View File

@@ -25,6 +25,7 @@ func getDecortValidator() *validator.Validate {
// registerAllValidators registers all custom validators in DecortValidator. // registerAllValidators registers all custom validators in DecortValidator.
func registerAllValidators(validate *validator.Validate) error { func registerAllValidators(validate *validator.Validate) error {
err := validate.RegisterValidation("proto", protoValidator) err := validate.RegisterValidation("proto", protoValidator)
if err != nil { if err != nil {
return err return err
@@ -105,6 +106,11 @@ func registerAllValidators(validate *validator.Validate) error {
return err return err
} }
err = validate.RegisterValidation("computex86NetType", computex86NetTypeValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("computeOrder", computeOrderValidator) err = validate.RegisterValidation("computeOrder", computeOrderValidator)
if err != nil { if err != nil {
return err return err
@@ -180,10 +186,75 @@ func registerAllValidators(validate *validator.Validate) error {
return err return err
} }
err = validate.RegisterValidation("interfaceTXModel", interfaceTXModelValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("interfaceIOEventFD", interfaceIOEventFDValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("interfaceEventIDx", interfaceEventIDxValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("workerGroupName", workerGroupNameValidator) err = validate.RegisterValidation("workerGroupName", workerGroupNameValidator)
if err != nil { if err != nil {
return err return err
} }
err = validate.RegisterValidation("sortBy", sortByValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("action", actionValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("vmaction", vmActionValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("mtu", mtuValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("computeFeatures", computeFeaturesValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("networkInterfaceNaming", networkInterfaceNamingValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("numaAffinity", numaAffinityValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("kvmx86NetType", kvmx86NetTypeValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("isBool", isBoolTypeValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("url", urlValidartor)
if err != nil {
return err
}
return nil return nil
} }

View File

@@ -1,7 +1,7 @@
package validators package validators
var ( var (
driverValues = []string{"KVM_X86", "KVM_PPC"} driverValues = []string{"KVM_X86"}
accessTypeValues = []string{"R", "RCX", "ARCXDU"} accessTypeValues = []string{"R", "RCX", "ARCXDU"}
resTypesValues = []string{"compute", "vins", "k8s", "openshift", "lb", "flipgroup"} resTypesValues = []string{"compute", "vins", "k8s", "openshift", "lb", "flipgroup"}
protoValues = []string{"tcp", "udp"} protoValues = []string{"tcp", "udp"}
@@ -10,38 +10,62 @@ var (
bserviceModeValues = []string{"ABSOLUTE", "RELATIVE"} bserviceModeValues = []string{"ABSOLUTE", "RELATIVE"}
computeTopologyValues = []string{"compute", "node"} computeTopologyValues = []string{"compute", "node"}
computePolicyValues = []string{"RECOMMENDED", "REQUIRED"} computePolicyValues = []string{"RECOMMENDED", "REQUIRED"}
computeModeValues = []string{"EQ", "EN", "ANY"} computeModeValues = []string{"EQ", "EN", "ANY"}
computeDiskTypeValues = []string{"D", "B"} computeDiskTypeValues = []string{"D", "B"}
computeNetTypeValues = []string{"EXTNET", "VINS"} computeNetTypeValues = []string{"EXTNET", "VINS"}
computeOrderValues = []string{"cdrom", "network", "hd"} computex86NetTypeValues = []string{"EXTNET", "VINS", "VFNIC", "DPDK"}
computeDataDisksValues = []string{"KEEP", "DETACH", "DESTROY"} computeOrderValues = []string{"cdrom", "network", "hd"}
computeDriverValues = []string{"KVM_X86", "SVA_KVM_X86"} computeDataDisksValues = []string{"KEEP", "DETACH", "DESTROY"}
computeDriverValues = []string{"KVM_X86", "SVA_KVM_X86"}
diskTypeValues = []string{"B", "T", "D"} diskTypeValues = []string{"B", "T", "D"}
flipgroupClientTypeValues = []string{"compute", "vins"} flipgroupClientTypeValues = []string{"compute", "vins"}
kvmNetTypeValues = []string{"EXTNET", "VINS", "NONE"} kvmNetTypeValues = []string{"EXTNET", "VINS", "NONE"}
kvmx86NetTypeValues = []string{"EXTNET", "VINS", "NONE", "VFNIC", "DPDK"}
lbAlgorithmValues = []string{"roundrobin", "static-rr", "leastconn"} lbAlgorithmValues = []string{"roundrobin", "static-rr", "leastconn"}
rgDefNetValues = []string{"PRIVATE", "PUBLIC", "NONE"} rgDefNetValues = []string{"PRIVATE", "PUBLIC", "NONE"}
rgNetTypeValues = []string{"PUBLIC", "PRIVATE"} rgNetTypeValues = []string{"PUBLIC", "PRIVATE"}
vinsTypeValues = []string{"DHCP", "VIP", "EXCLUDE"} vinsTypeValues = []string{"DHCP", "VIP", "EXCLUDED"}
imageBootTypeValues = []string{"uefi", "bios"} imageBootTypeValues = []string{"uefi", "bios"}
imageTypeValues = []string{"windows", "linux", "other"} imageTypeValues = []string{"windows", "linux", "other"}
imageDriversValues = []string{"KVM_X86"} imageDriversValues = []string{"KVM_X86"}
imageArchitectureValues = []string{"X86_64", "PPC64_LE"} imageArchitectureValues = []string{"X86_64"}
sepFieldTypeValues = []string{"int", "str", "bool", "list", "dict"} sepFieldTypeValues = []string{"int", "str", "bool", "list", "dict"}
networkPluginValues = []string{"flannel", "weawenet", "calico"} networkPluginValues = []string{"flannel", "weavenet", "calico"}
strictLooseValues = []string{"strict", "loose"} strictLooseValues = []string{"strict", "loose"}
interfaceStateValues = []string{"on", "off"} interfaceStateValues = []string{"on", "off"}
actionValues = []string{"power_on", "shutdown", "force_shutdown", "reboot"}
vmActionValues = []string{"stop", "move"}
computeFeaturesValues = []string{"hugepages", "numa", "cpupin", "vfnic"}
networkInterfaceNamingValues = []string{"eth", "ens"}
numaAffinityValues = []string{"none", "strict", "loose"}
txModelValues = []string{"iothread", "timer", "selected by hypervisor"}
ioEventFDValues = []string{"on", "off", "selected by hypervisor"}
eventIDxValues = []string{"on", "off", "selected by hypervisor"}
)
const (
mtuMin = 1
mtuMax = 9216
) )

View File

@@ -4,26 +4,22 @@ import (
"bytes" "bytes"
"context" "context"
"crypto/tls" "crypto/tls"
"errors"
"fmt" "fmt"
"io" "io"
"mime/multipart"
"net/http" "net/http"
"net/url" "net/url"
"strconv"
"strings" "strings"
"sync" "sync"
"time" "time"
"github.com/google/go-querystring/query" "github.com/google/go-querystring/query"
"repository.basistech.ru/BASIS/decort-golang-sdk/config" "repository.basistech.ru/BASIS/decort-golang-sdk/config"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/constants"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi" "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" "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker"
k8s_cb "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/k8s"
) )
// Legacy HTTP-client for platform // LegacyDecortClient is Legacy HTTP-client for platform
type LegacyDecortClient struct { type LegacyDecortClient struct {
decortURL string decortURL string
client *http.Client client *http.Client
@@ -54,7 +50,7 @@ func NewLegacy(cfg config.LegacyConfig) *LegacyDecortClient {
}, },
}, },
}, },
cfg: cfg, cfg: trimLegacyConfig(&cfg),
expiryTime: expiryTime, expiryTime: expiryTime,
mutex: &sync.Mutex{}, mutex: &sync.Mutex{},
} }
@@ -72,20 +68,18 @@ func (ldc *LegacyDecortClient) CloudBroker() *cloudbroker.CloudBroker {
// DecortApiCall method for sending requests to the platform // DecortApiCall method for sending requests to the platform
func (ldc *LegacyDecortClient) DecortApiCall(ctx context.Context, method, url string, params interface{}) ([]byte, error) { func (ldc *LegacyDecortClient) DecortApiCall(ctx context.Context, method, url string, params interface{}) ([]byte, error) {
// get token
if err := ldc.getToken(ctx); err != nil { if err := ldc.getToken(ctx); err != nil {
return nil, err return nil, err
} }
k8sCaCreateReq, okCa := params.(k8s_ca.CreateRequest)
k8sCbCreateReq, okCb := params.(k8s_cb.CreateRequest)
var body *bytes.Buffer var body *bytes.Buffer
var ctype string var ctype string
if okCa { byteSlice, ok := params.([]byte)
body, ctype = createK8sCloudApiLegacy(k8sCaCreateReq, ldc.cfg.Token) if ok {
} else if okCb { body = bytes.NewBuffer(byteSlice)
body, ctype = createK8sCloudBrokerLegacy(k8sCbCreateReq, ldc.cfg.Token) ctype = "application/octet-stream"
} else { } else {
values, err := query.Values(params) values, err := query.Values(params)
if err != nil { if err != nil {
@@ -93,320 +87,162 @@ func (ldc *LegacyDecortClient) DecortApiCall(ctx context.Context, method, url st
} }
body = bytes.NewBufferString(values.Encode() + fmt.Sprintf("&authkey=%s", ldc.cfg.Token)) 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+constants.RESTMACHINE+url, body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
resp, err := ldc.do(req, ctype) // perform request
if err != nil { respBytes, err := ldc.do(req, ctype)
return nil, err
}
defer resp.Body.Close()
respBytes, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if resp.StatusCode != 200 { return respBytes, err
return nil, errors.New(string(respBytes)) }
func (ldc *LegacyDecortClient) DecortApiCallMP(ctx context.Context, method, url string, params interface{}) ([]byte, error) {
body, ctype, err := multiPartReq(params)
if err != nil {
return nil, err
} }
return respBytes, nil req, err := http.NewRequestWithContext(ctx, method, ldc.decortURL+constants.RESTMACHINE+url, body)
if err != nil {
return nil, err
}
// get token
if err = ldc.getToken(ctx); err != nil {
return nil, err
}
// perform request
respBytes, err := ldc.do(req, ctype)
if err != nil {
return nil, err
}
return respBytes, err
} }
func (ldc *LegacyDecortClient) getToken(ctx context.Context) error { func (ldc *LegacyDecortClient) getToken(ctx context.Context) error {
ldc.mutex.Lock() ldc.mutex.Lock()
defer ldc.mutex.Unlock() defer ldc.mutex.Unlock()
if ldc.cfg.Token == "" || time.Now().After(ldc.expiryTime) { // new token is not needed
body := fmt.Sprintf("username=%s&password=%s", url.QueryEscape(ldc.cfg.Username), url.QueryEscape(ldc.cfg.Password)) if ldc.cfg.Token != "" && !time.Now().After(ldc.expiryTime) {
bodyReader := strings.NewReader(body) return nil
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)
} }
// set up request headers and body
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+constants.RESTMACHINE+"/cloudapi/user/authenticate", bodyReader)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
// request token
resp, err := ldc.client.Do(req)
if err != nil || resp == nil {
return fmt.Errorf("cannot get token: %w", err)
}
defer resp.Body.Close()
var tokenBytes []byte
tokenBytes, err = io.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("cannot get token: %w", err)
}
if resp.StatusCode != 200 {
return fmt.Errorf("cannot get token: %s", tokenBytes)
}
// save token in config
token := string(tokenBytes)
ldc.cfg.Token = token
ldc.expiryTime = time.Now().AddDate(0, 0, 1)
return nil return nil
} }
func (ldc *LegacyDecortClient) do(req *http.Request, ctype string) (*http.Response, error) { // do method performs request and returns response as an array of bytes and nil error in case of response status code 200.
// In any other cases do returns nil response and error.
// Retries are implemented in case of connection reset errors.
func (ldc *LegacyDecortClient) do(req *http.Request, ctype string) ([]byte, error) {
// set up request headers and body
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
if ctype != "" { if ctype != "" {
req.Header.Add("Content-Type", ctype) req.Header.Set("Content-Type", ctype)
} else {
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
} }
req.Header.Set("Accept", "application/json") req.Header.Set("Accept", "application/json")
// var resp *http.Response buf, err := io.ReadAll(req.Body)
// var err error if err != nil {
buf, _ := io.ReadAll(req.Body) return nil, err
// req = req.Clone(req.Context())
// for i := uint64(0); i < ldc.cfg.Retries; i++ {
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()
// }
// }
req.Body.Close()
req.Body = io.NopCloser(bytes.NewBuffer(buf))
resp, err := ldc.client.Do(req)
if resp != nil {
defer resp.Body.Close()
}
// retries logic GOES HERE
// get http response
//var resp *http.Response
//for i := uint64(0); i < ldc.cfg.Retries; i++ {
// req := req.Clone(req.Context())
// req.Body = io.NopCloser(bytes.NewBuffer(buf))
//
// if i > 0 {
// time.Sleep(5 * time.Second) // no time sleep for the first request
// }
//
// resp, err = ldc.client.Do(req)
//
// // stop retries on success and close response body
// if resp != nil {
// defer resp.Body.Close()
// }
// if err == nil {
// break
// }
//
// // retries in case of connection errors with time sleep
// if isConnectionError(err) {
// continue
// }
//
// // return error in case of non-connection error
// return nil, err
//}
// handle http request errors
if err != nil {
return nil, err
}
if resp == nil {
return nil, fmt.Errorf("got empty response without error")
}
// handle successful request
respBytes, _ := io.ReadAll(resp.Body)
if resp.StatusCode == 200 {
return respBytes, nil
}
// handle errors with status code other than 200
err = fmt.Errorf("%s", respBytes)
return nil, fmt.Errorf("could not execute request: %w", err) return nil, fmt.Errorf("could not execute request: %w", err)
} }
func createK8sCloudApiLegacy(req k8s_ca.CreateRequest, token string) (*bytes.Buffer, string) { func trimLegacyConfig(cfg *config.LegacyConfig) config.LegacyConfig {
reqBody := &bytes.Buffer{} cfg.DecortURL = strings.TrimSuffix(cfg.DecortURL, "/")
writer := multipart.NewWriter(reqBody) return *cfg
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))
_ = writer.WriteField("authkey", token)
ct := writer.FormDataContentType()
writer.Close()
return reqBody, ct
}
func createK8sCloudBrokerLegacy(req k8s_cb.CreateRequest, token string) (*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))
_ = writer.WriteField("authkey", token)
ct := writer.FormDataContentType()
writer.Close()
return reqBody, ct
} }

View File

@@ -17,10 +17,6 @@ type DeleteUserRequest struct {
// ID or emailaddress of the user to remove // ID or emailaddress of the user to remove
// Required: true // Required: true
UserID string `url:"userId" json:"userId" validate:"required"` UserID string `url:"userId" json:"userId" validate:"required"`
// Recursively revoke access rights from owned cloudspaces and vmachines
// Required: false
RecursiveDelete bool `url:"recursivedelete" json:"recursivedelete"`
} }
// DeleteUser revokes user access from the account // DeleteUser revokes user access from the account

View File

@@ -1,58 +0,0 @@
package account
import (
"context"
"net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// GetConsumptionRequest struct to download the resources tracking files for an account
type GetConsumptionRequest struct {
// ID an account
// Required: true
AccountID uint64 `url:"accountId" json:"accountId" validate:"required"`
// Epoch represents the start time
// Required: true
Start uint64 `url:"start" json:"start" validate:"required"`
// Epoch represents the end time
// Required: true
End uint64 `url:"end" json:"end" validate:"required"`
}
// GetConsumption downloads the resources tracking files for an account within a given period
func (a Account) GetConsumption(ctx context.Context, req GetConsumptionRequest) (string, error) {
err := validators.ValidateRequest(req)
if err != nil {
return "", validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/account/getConsumption"
res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil {
return "", err
}
return string(res), nil
}
// GetConsumptionGet downloads the resources tracking files for an account within a given period
func (a Account) GetConsumptionGet(ctx context.Context, req GetConsumptionRequest) (string, error) {
err := validators.ValidateRequest(req)
if err != nil {
return "", validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/account/getConsumption"
res, err := a.client.DecortApiCall(ctx, http.MethodGet, url, req)
if err != nil {
return "", err
}
return string(res), nil
}

View File

@@ -4,6 +4,8 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"net/http" "net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// ListRequest struct to get list of accounts // ListRequest struct to get list of accounts
@@ -24,6 +26,10 @@ type ListRequest struct {
// Required: false // Required: false
Status string `url:"status,omitempty" json:"status,omitempty"` Status string `url:"status,omitempty" json:"status,omitempty"`
// Sort by one of supported fields, format +|-(field)
// Required: false
SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"`
// Page number // Page number
// Required: false // Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"` Page uint64 `url:"page,omitempty" json:"page,omitempty"`
@@ -35,6 +41,7 @@ type ListRequest struct {
// List gets a list of all accounts the user has access to a ListAccounts struct // List gets a list of all accounts the user has access to a ListAccounts struct
func (a Account) List(ctx context.Context, req ListRequest) (*ListAccounts, error) { func (a Account) List(ctx context.Context, req ListRequest) (*ListAccounts, error) {
res, err := a.ListRaw(ctx, req) res, err := a.ListRaw(ctx, req)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -52,6 +59,11 @@ func (a Account) List(ctx context.Context, req ListRequest) (*ListAccounts, erro
// ListRaw gets a list of all accounts the user has access to as an array of bytes // ListRaw gets a list of all accounts the user has access to as an array of bytes
func (a Account) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { func (a Account) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) {
if err := validators.ValidateRequest(req); err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/account/list" url := "/cloudapi/account/list"
res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req)

View File

@@ -46,6 +46,10 @@ type ListComputesRequest struct {
// Required: false // Required: false
ExtNetID uint64 `url:"extNetId,omitempty" json:"extNetId,omitempty"` ExtNetID uint64 `url:"extNetId,omitempty" json:"extNetId,omitempty"`
// Sort by one of supported fields, format +|-(field)
// Required: false
SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"`
// Page number // Page number
// Required: false // Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"` Page uint64 `url:"page,omitempty" json:"page,omitempty"`
@@ -57,6 +61,7 @@ type ListComputesRequest struct {
// ListComputes gets list all compute instances under specified account, accessible by the user // ListComputes gets list all compute instances under specified account, accessible by the user
func (a Account) ListComputes(ctx context.Context, req ListComputesRequest) (*ListComputes, error) { func (a Account) ListComputes(ctx context.Context, req ListComputesRequest) (*ListComputes, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err)) return nil, validators.ValidationErrors(validators.GetErrors(err))

View File

@@ -4,6 +4,8 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"net/http" "net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// ListDeletedRequest struct to get a list of deleted accounts // ListDeletedRequest struct to get a list of deleted accounts
@@ -27,10 +29,20 @@ type ListDeletedRequest struct {
// Find by access control list // Find by access control list
// Required: false // Required: false
ACL string `url:"acl,omitempty" json:"acl,omitempty"` ACL string `url:"acl,omitempty" json:"acl,omitempty"`
// Sort by one of supported fields, format +|-(field)
// Required: false
SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"`
} }
// ListDeleted gets list of all deleted accounts the user has access to // ListDeleted gets list of all deleted accounts the user has access to
func (a Account) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListAccounts, error) { func (a Account) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListAccounts, error) {
err := validators.ValidateRequest(req)
if err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/account/listDeleted" url := "/cloudapi/account/listDeleted"
res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req)

View File

@@ -30,6 +30,10 @@ type ListDisksRequest struct {
// Required: false // Required: false
Type string `url:"type,omitempty" json:"type,omitempty"` Type string `url:"type,omitempty" json:"type,omitempty"`
// Sort by one of supported fields, format +|-(field)
// Required: false
SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"`
// Page number // Page number
// Required: false // Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"` Page uint64 `url:"page,omitempty" json:"page,omitempty"`
@@ -41,6 +45,7 @@ type ListDisksRequest struct {
// ListDisks gets list all currently unattached disks under specified account // ListDisks gets list all currently unattached disks under specified account
func (a Account) ListDisks(ctx context.Context, req ListDisksRequest) (*ListDisks, error) { func (a Account) ListDisks(ctx context.Context, req ListDisksRequest) (*ListDisks, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err)) return nil, validators.ValidationErrors(validators.GetErrors(err))

View File

@@ -38,6 +38,10 @@ type ListFLIPGroupsRequest struct {
// Required: false // Required: false
FLIPGroupID uint64 `url:"flipGroupId,omitempty" json:"flipGroupId,omitempty"` FLIPGroupID uint64 `url:"flipGroupId,omitempty" json:"flipGroupId,omitempty"`
// Sort by one of supported fields, format +|-(field)
// Required: false
SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"`
// Page number // Page number
// Required: false // Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"` Page uint64 `url:"page,omitempty" json:"page,omitempty"`
@@ -49,6 +53,7 @@ type ListFLIPGroupsRequest struct {
// ListFLIPGroups gets list all FLIPGroups under specified account, accessible by the user // ListFLIPGroups gets list all FLIPGroups under specified account, accessible by the user
func (a Account) ListFLIPGroups(ctx context.Context, req ListFLIPGroupsRequest) (*ListFLIPGroups, error) { func (a Account) ListFLIPGroups(ctx context.Context, req ListFLIPGroupsRequest) (*ListFLIPGroups, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err)) return nil, validators.ValidationErrors(validators.GetErrors(err))

View File

@@ -41,10 +41,15 @@ type ListRGRequest struct {
// Find by status // Find by status
// Required: false // Required: false
Status string `url:"status,omitempty" json:"status,omitempty"` Status string `url:"status,omitempty" json:"status,omitempty"`
// Sort by one of supported fields, format +|-(field)
// Required: false
SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"`
} }
// ListRG gets list of all resource groups under specified account, accessible by the user // ListRG gets list of all resource groups under specified account, accessible by the user
func (a Account) ListRG(ctx context.Context, req ListRGRequest) (*ListRG, error) { func (a Account) ListRG(ctx context.Context, req ListRGRequest) (*ListRG, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err)) return nil, validators.ValidationErrors(validators.GetErrors(err))

View File

@@ -30,6 +30,10 @@ type ListTemplatesRequest struct {
// Required: false // Required: false
Type string `url:"type,omitempty" json:"type,omitempty"` Type string `url:"type,omitempty" json:"type,omitempty"`
// Sort by one of supported fields, format +|-(field)
// Required: false
SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"`
// Page number // Page number
// Required: false // Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"` Page uint64 `url:"page,omitempty" json:"page,omitempty"`
@@ -41,6 +45,7 @@ type ListTemplatesRequest struct {
// ListTemplates gets list of templates which can be managed by this account // ListTemplates gets list of templates which can be managed by this account
func (a Account) ListTemplates(ctx context.Context, req ListTemplatesRequest) (*ListTemplates, error) { func (a Account) ListTemplates(ctx context.Context, req ListTemplatesRequest) (*ListTemplates, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err)) return nil, validators.ValidationErrors(validators.GetErrors(err))

View File

@@ -30,6 +30,10 @@ type ListVINSRequest struct {
// Required: false // Required: false
ExtIP string `url:"extIp,omitempty" json:"extIp,omitempty"` ExtIP string `url:"extIp,omitempty" json:"extIp,omitempty"`
// Sort by one of supported fields, format +|-(field)
// Required: false
SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"`
// Page number // Page number
// Required: false // Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"` Page uint64 `url:"page,omitempty" json:"page,omitempty"`
@@ -41,6 +45,7 @@ type ListVINSRequest struct {
// ListVINS gets list of all ViNSes under specified account, accessible by the user // ListVINS gets list of all ViNSes under specified account, accessible by the user
func (a Account) ListVINS(ctx context.Context, req ListVINSRequest) (*ListVINS, error) { func (a Account) ListVINS(ctx context.Context, req ListVINSRequest) (*ListVINS, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err)) return nil, validators.ValidationErrors(validators.GetErrors(err))

View File

@@ -53,6 +53,9 @@ type ItemAccount struct {
// Access Control List // Access Control List
ACL []RecordACL `json:"acl"` ACL []RecordACL `json:"acl"`
// Compute Features
ComputeFeatures []string `json:"computeFeatures"`
// Created time // Created time
CreatedTime uint64 `json:"createdTime"` CreatedTime uint64 `json:"createdTime"`
@@ -176,6 +179,9 @@ type RecordAccount struct {
// Company URL // Company URL
CompanyURL string `json:"companyurl"` CompanyURL string `json:"companyurl"`
// Compute Features
ComputeFeatures []string `json:"computeFeatures"`
// Computes // Computes
Computes Computes `json:"computes"` Computes Computes `json:"computes"`
@@ -374,6 +380,12 @@ type ItemVINS struct {
// External IP // External IP
ExternalIP string `json:"externalIP"` ExternalIP string `json:"externalIP"`
// Extnet ID
ExtnetId uint64 `json:"extnetId"`
// Free IPs
FreeIPs int64 `json:"freeIPs"`
// ID // ID
ID uint64 `json:"id"` ID uint64 `json:"id"`

View File

@@ -45,6 +45,11 @@ type UpdateRequest struct {
// Limit (positive) or disable (0) GPU resources // Limit (positive) or disable (0) GPU resources
// Required: false // Required: false
GPUUnits int64 `url:"gpu_units,omitempty" json:"gpu_units,omitempty"` GPUUnits int64 `url:"gpu_units,omitempty" json:"gpu_units,omitempty"`
// List of strings with pools
// i.e.: ["sep1_poolName1", "sep2_poolName2", etc]
// Required: false
UniqPools []string `url:"uniqPools,omitempty" json:"uniqPools,omitempty"`
} }
// Update updates an account name and resource types and limits // Update updates an account name and resource types and limits

10
pkg/cloudapi/audit.go Normal file
View File

@@ -0,0 +1,10 @@
package cloudapi
import (
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/audit"
)
// Accessing the Stack method group
func (ca *CloudAPI) Audit() *audit.Audit {
return audit.New(ca.client)
}

View File

@@ -0,0 +1,15 @@
package audit
import "repository.basistech.ru/BASIS/decort-golang-sdk/interfaces"
// Structure for creating request to audit
type Audit struct {
client interfaces.Caller
}
// Builder for audit endpoint
func New(client interfaces.Caller) *Audit{
return &Audit{
client: client,
}
}

46
pkg/cloudapi/audit/get.go Normal file
View File

@@ -0,0 +1,46 @@
package audit
import (
"context"
"encoding/json"
"net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// GetRequest struct to get information about account
type GetRequest struct {
// Audit GUID
// Required: true
AuditGuid string `url:"auditGuid" json:"auditGuid" validate:"required"`
}
// Get gets information about audit as a RecordAudit struct
func (a Audit) Get(ctx context.Context, req GetRequest) (*RecordAudit, error) {
res, err := a.GetRaw(ctx, req)
if err != nil {
return nil, err
}
info := RecordAudit{}
err = json.Unmarshal(res, &info)
if err != nil {
return nil, err
}
return &info, nil
}
// GetRaw gets information about audit as an array of bytes
func (a Audit) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) {
err := validators.ValidateRequest(req)
if err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/audit/get"
res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req)
return res, err
}

View File

@@ -0,0 +1,41 @@
package audit
// Main info about audit
type RecordAudit struct {
// Arguments
Arguments string `json:"args"`
// Call
Call string `json:"call"`
// GUID
GUID string `json:"guid"`
// Kwargs
Kwargs string `json:"kwargs"`
// RemoteAddr
RemoteAddr string `json:"remote_addr"`
// Response time
ResponseTime float64 `json:"responsetime"`
// Result
Result string `json:"result"`
// Status code
StatusCode uint64 `json:"statuscode"`
// Tags
Tags string `json:"tags"`
// Timestamp
Timestamp float64 `json:"timestamp"`
// TimestampEnd
TimestampEnd float64 `json:"timestampEnd"`
// User
User string `json:"user"`
}

View File

@@ -24,7 +24,7 @@ func (b BService) Disable(ctx context.Context, req DisableRequest) (bool, error)
return false, validators.ValidationErrors(validators.GetErrors(err)) return false, validators.ValidationErrors(validators.GetErrors(err))
} }
url := "/cloudapi/bservice/delete" url := "/cloudapi/bservice/disable"
res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil { if err != nil {

View File

@@ -41,7 +41,6 @@ type GroupAddRequest struct {
// Compute driver // Compute driver
// should be one of: // should be one of:
// - KVM_X86 // - KVM_X86
// - KVM_PPC
// Required: true // Required: true
Driver string `url:"driver" json:"driver" validate:"driver"` Driver string `url:"driver" json:"driver" validate:"driver"`
@@ -74,6 +73,16 @@ type GroupAddRequest struct {
UserData string `url:"userData,omitempty" json:"userData,omitempty"` UserData string `url:"userData,omitempty" json:"userData,omitempty"`
} }
// GetRAM returns RAM field values
func (r GroupAddRequest) GetRAM() map[string]uint64 {
res := make(map[string]uint64, 1)
res["RAM"] = r.RAM
return res
}
// GroupAdd creates new Compute Group within BasicService. // GroupAdd creates new Compute Group within BasicService.
// Compute Group is NOT started automatically, // Compute Group is NOT started automatically,
// so you need to explicitly start it // so you need to explicitly start it

View File

@@ -43,6 +43,16 @@ type GroupUpdateRequest struct {
Force bool `url:"force,omitempty" json:"force,omitempty"` Force bool `url:"force,omitempty" json:"force,omitempty"`
} }
// GetRAM returns RAM field values
func (r GroupUpdateRequest) GetRAM() map[string]uint64 {
res := make(map[string]uint64, 1)
res["RAM"] = r.RAM
return res
}
// GroupUpdate updates existing Compute group within Basic Service and apply new settings to its computes as necessary // GroupUpdate updates existing Compute group within Basic Service and apply new settings to its computes as necessary
func (b BService) GroupUpdate(ctx context.Context, req GroupUpdateRequest) (bool, error) { func (b BService) GroupUpdate(ctx context.Context, req GroupUpdateRequest) (bool, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)

View File

@@ -4,6 +4,8 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"net/http" "net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// ListRequest struct to get list of BasicService instances // ListRequest struct to get list of BasicService instances
@@ -40,6 +42,10 @@ type ListRequest struct {
// Required: false // Required: false
AccountName string `url:"accountName,omitempty" json:"accountName,omitempty"` AccountName string `url:"accountName,omitempty" json:"accountName,omitempty"`
// Sort by one of supported fields, format +|-(field)
// Required: false
SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"`
// Page number // Page number
// Required: false // Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"` Page uint64 `url:"page,omitempty" json:"page,omitempty"`
@@ -51,6 +57,7 @@ type ListRequest struct {
// List gets list of BasicService instances associated with the specified Resource Group as a ListBasicServices struct // List gets list of BasicService instances associated with the specified Resource Group as a ListBasicServices struct
func (b BService) List(ctx context.Context, req ListRequest) (*ListBasicServices, error) { func (b BService) List(ctx context.Context, req ListRequest) (*ListBasicServices, error) {
res, err := b.ListRaw(ctx, req) res, err := b.ListRaw(ctx, req)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -68,6 +75,11 @@ func (b BService) List(ctx context.Context, req ListRequest) (*ListBasicServices
// ListRaw gets list of BasicService instances associated with the specified Resource Group as an array of bytes // ListRaw gets list of BasicService instances associated with the specified Resource Group as an array of bytes
func (b BService) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { func (b BService) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) {
if err := validators.ValidateRequest(req); err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/bservice/list" url := "/cloudapi/bservice/list"
res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req)

View File

@@ -4,6 +4,8 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"net/http" "net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// ListDeletedRequest struct to get list of deleted BasicService instances // ListDeletedRequest struct to get list of deleted BasicService instances
@@ -16,6 +18,10 @@ type ListDeletedRequest struct {
// Required: false // Required: false
RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"`
// Sort by one of supported fields, format +|-(field)
// Required: false
SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"`
// Page number // Page number
// Required: false // Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"` Page uint64 `url:"page,omitempty" json:"page,omitempty"`
@@ -27,6 +33,11 @@ type ListDeletedRequest struct {
// ListDeleted gets list of deleted BasicService instances associated with the specified Resource Group // ListDeleted gets list of deleted BasicService instances associated with the specified Resource Group
func (b BService) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListBasicServices, error) { func (b BService) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListBasicServices, error) {
if err := validators.ValidateRequest(req); err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/bservice/listDeleted" url := "/cloudapi/bservice/listDeleted"
res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req) res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req)

View File

@@ -93,7 +93,7 @@ type RecordBasicService struct {
// Main information about Compute // Main information about Compute
type ItemCompute struct { type ItemCompute struct {
// Account ID // Account ID
AccountID uint64 AccountID uint64 `json:"accountId"`
// Architecture // Architecture
Architecture string `json:"arch"` Architecture string `json:"arch"`
@@ -168,9 +168,18 @@ type ItemSnapshot struct {
Valid bool `json:"valid"` Valid bool `json:"valid"`
} }
// List of Snapshots // List of Snapshot
type ListSnapshots []ItemSnapshot type ListSnapshots []ItemSnapshot
// List of Snapshots
type ListInfoSnapshots struct {
// Data
Data ListSnapshots `json:"data"`
// EntryCount
EntryCount uint64 `json:"entryCount"`
}
// Main information about Group // Main information about Group
type RecordGroup struct { type RecordGroup struct {
// Account ID // Account ID

View File

@@ -16,7 +16,7 @@ type SnapshotListRequest struct {
} }
// SnapshotList gets list existing snapshots of the Basic Service // 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) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err)) return nil, validators.ValidationErrors(validators.GetErrors(err))
@@ -29,12 +29,12 @@ func (b BService) SnapshotList(ctx context.Context, req SnapshotListRequest) (Li
return nil, err return nil, err
} }
list := ListSnapshots{} list := ListInfoSnapshots{}
err = json.Unmarshal(res, &list) err = json.Unmarshal(res, &list)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return list, nil return &list, nil
} }

View File

@@ -38,8 +38,9 @@ type AffinityRuleAddRequest struct {
Key string `url:"key" json:"key" validate:"required"` Key string `url:"key" json:"key" validate:"required"`
// Value that must match the key to be taken into account when analyzing this rule // Value that must match the key to be taken into account when analyzing this rule
// Required: true // Required: false
Value string `url:"value" json:"value" validate:"required"` // Not required on purpose: despite required tag on platform, empty string is allowed
Value string `url:"value" json:"value"`
} }
// AffinityRuleAdd add affinity rule // AffinityRuleAdd add affinity rule

View File

@@ -38,8 +38,9 @@ type AffinityRuleRemoveRequest struct {
Key string `url:"key" json:"key" validate:"required"` Key string `url:"key" json:"key" validate:"required"`
// Value that must match the key to be taken into account when analyzing this rule // Value that must match the key to be taken into account when analyzing this rule
// Required: true // Required: false
Value string `url:"value" json:"value" validate:"required"` // Not required on purpose: despite required tag on platform, empty string is allowed
Value string `url:"value" json:"value"`
} }
// AffinityRuleRemove remove affinity rule // AffinityRuleRemove remove affinity rule

View File

@@ -38,8 +38,9 @@ type AntiAffinityRuleAddRequest struct {
Key string `url:"key" json:"key" validate:"required"` Key string `url:"key" json:"key" validate:"required"`
// Value that must match the key to be taken into account when analyzing this rule // Value that must match the key to be taken into account when analyzing this rule
// Required: true // Required: false
Value string `url:"value" json:"value" validate:"required"` // Not required on purpose: despite required tag on platform, empty string is allowed
Value string `url:"value" json:"value"`
} }
// AntiAffinityRuleAdd add anti affinity rule // AntiAffinityRuleAdd add anti affinity rule

View File

@@ -38,8 +38,9 @@ type AntiAffinityRuleRemoveRequest struct {
Key string `url:"key" json:"key" validate:"required"` Key string `url:"key" json:"key" validate:"required"`
// Value that must match the key to be taken into account when analyzing this rule // Value that must match the key to be taken into account when analyzing this rule
// Required: true // Required: false
Value string `url:"value" json:"value" validate:"required"` // Not required on purpose: despite required tag on platform, empty string is allowed
Value string `url:"value" json:"value"`
} }
// AntiAffinityRuleRemove remove anti affinity rule // AntiAffinityRuleRemove remove anti affinity rule

View File

@@ -0,0 +1,42 @@
package compute
import (
"context"
"net/http"
"strconv"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// BootDiskSetRequest struct to set boot disk for compute
type BootDiskSetRequest struct {
// ID of compute instance
// Required: true
ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"`
// ID of the disk to set as boot
// Required: true
DiskID uint64 `url:"diskId" json:"diskId" validate:"required"`
}
// BootDiskSet sets boot disk for compute
func (c Compute) BootDiskSet(ctx context.Context, req BootDiskSetRequest) (bool, error) {
err := validators.ValidateRequest(req)
if err != nil {
return false, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/compute/bootDiskSet"
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil {
return false, err
}
result, err := strconv.ParseBool(string(res))
if err != nil {
return false, err
}
return result, nil
}

View File

@@ -0,0 +1,54 @@
package compute
import (
"context"
"net/http"
"strconv"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// ChangeIPRequest struct to change IP for network
type ChangeIPRequest struct {
// ID of compute instance
// Required: true
ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"`
// Network type
// 'EXTNET' for connect to external network directly
// 'VINS' for connect to ViNS
// Required: true
NetType string `url:"netType" json:"netType" validate:"computeNetType"`
// Network ID for connect to
// For EXTNET - external network ID
// For VINS - VINS ID
// Required: true
NetID uint64 `url:"netId" json:"netId" validate:"required"`
// IP address to which we will change the existing one, it must be from the same subnet
// Required: true
IPAddr string `url:"ipAddr" json:"ipAddr" validate:"required"`
}
// ChangeIP change reserved IP for compute instance
func (c Compute) ChangeIP(ctx context.Context, req ChangeIPRequest) (bool, error) {
err := validators.ValidateRequest(req)
if err != nil {
return false, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/compute/changeIp"
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil {
return false, err
}
result, err := strconv.ParseBool(string(res))
if err != nil {
return false, err
}
return result, nil
}

View File

@@ -25,6 +25,11 @@ type CloneRequest struct {
// Name of the parent's snapshot to create clone from // Name of the parent's snapshot to create clone from
// Required: false // Required: false
SnapshotName string `url:"snapshotName,omitempty" json:"snapshotName,omitempty"` SnapshotName string `url:"snapshotName,omitempty" json:"snapshotName,omitempty"`
// true ignore that the compute is running
// Default: false
// Required: false
Force bool `url:"force" json:"force"`
} }
// Clone clones compute instance // Clone clones compute instance

View File

@@ -23,7 +23,7 @@ type CreateTemplateRequest struct {
type wrapperCreateTemplateRequest struct { type wrapperCreateTemplateRequest struct {
CreateTemplateRequest CreateTemplateRequest
Async bool `url:"async"` AsyncMode bool `url:"asyncMode"`
} }
// CreateTemplate create template from compute instance // CreateTemplate create template from compute instance
@@ -35,7 +35,7 @@ func (c Compute) CreateTemplate(ctx context.Context, req CreateTemplateRequest)
reqWrapped := wrapperCreateTemplateRequest{ reqWrapped := wrapperCreateTemplateRequest{
CreateTemplateRequest: req, CreateTemplateRequest: req,
Async: false, AsyncMode: false,
} }
url := "/cloudapi/compute/createTemplate" url := "/cloudapi/compute/createTemplate"
@@ -62,7 +62,7 @@ func (c Compute) CreateTemplateAsync(ctx context.Context, req CreateTemplateRequ
reqWrapped := wrapperCreateTemplateRequest{ reqWrapped := wrapperCreateTemplateRequest{
CreateTemplateRequest: req, CreateTemplateRequest: req,
Async: true, AsyncMode: true,
} }
url := "/cloudapi/compute/createTemplate" url := "/cloudapi/compute/createTemplate"

View File

@@ -0,0 +1,112 @@
package compute
import (
"context"
"net/http"
"strconv"
"strings"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// CreateTemplateFromBlankRequest struct to create template from boot disk of current compute
type CreateTemplateFromBlankRequest struct {
// ID of the compute to create template from
// Required: true
ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"`
// Name of the rescue disk
// Required: true
Name string `url:"name" json:"name" validate:"required"`
// Boot type of image BIOS or UEFI
// Required: true
BootType string `url:"boottype" json:"boottype" validate:"imageBootType"`
// Image type linux, windows or other
// Required: true
ImageType string `url:"imagetype" json:"imagetype" validate:"imageType"`
// Username for the image
// Required: false
Username string `url:"username,omitempty" json:"username,omitempty"`
// Password for the image
// Required: false
Password string `url:"password,omitempty" json:"password,omitempty"`
// Account ID to make the image exclusive
// Required: false
AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"`
// SEP ID
// Required: false
SepID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"`
// Pool for image create
// Required: false
PoolName string `url:"poolName,omitempty" json:"poolName,omitempty"`
// Does this machine supports hot resize
// Default: false
// Required: false
HotResize bool `url:"hotresize" json:"hotresize"`
}
type wrapperCreateTemplateFromBlankRequest struct {
CreateTemplateFromBlankRequest
AsyncMode bool `url:"asyncMode"`
}
// CreateTemplateFromBlank creates template from boot disk of current compute in sync mode.
// It returns id of created compute and error.
func (c Compute) CreateTemplateFromBlank(ctx context.Context, req CreateTemplateFromBlankRequest) (uint64, error) {
err := validators.ValidateRequest(req)
if err != nil {
return 0, validators.ValidationErrors(validators.GetErrors(err))
}
reqWrapped := wrapperCreateTemplateFromBlankRequest{
CreateTemplateFromBlankRequest: req,
AsyncMode: false,
}
url := "/cloudapi/compute/createTemplateFromBlank"
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped)
if err != nil {
return 0, err
}
result, err := strconv.ParseUint(string(res), 10, 64)
if err != nil {
return 0, err
}
return result, nil
}
// CreateTemplateFromBlankAsync creates template from boot disk of current compute in async mode.
// It returns guid of task and error.
func (c Compute) CreateTemplateFromBlankAsync(ctx context.Context, req CreateTemplateFromBlankRequest) (string, error) {
err := validators.ValidateRequest(req)
if err != nil {
return "", validators.ValidationErrors(validators.GetErrors(err))
}
reqWrapped := wrapperCreateTemplateFromBlankRequest{
CreateTemplateFromBlankRequest: req,
AsyncMode: true,
}
url := "/cloudapi/compute/createTemplateFromBlank"
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped)
if err != nil {
return "", err
}
result := strings.ReplaceAll(string(res), "\"", "")
return result, nil
}

View File

@@ -0,0 +1,53 @@
package compute
import (
"context"
"net/http"
"strconv"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// DiskMigrateRequest struct to migrate compute's disk to target disk
type DiskMigrateRequest struct {
// ID of compute instance
// Required: true
ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"`
// ID source disk
// Required: true
DiskID uint64 `url:"diskId" json:"diskId" validate:"required"`
// ID target disk
// Required: true
TargetDiskID uint64 `url:"targetDiskId" json:"targetDiskId" validate:"required"`
// Migration mode. 1 - Data migration and domain update were already completed by third-party software.
// Use this if target disk already connected to compute and you only need to save changes for next reboot.
// Required: false
Mode int64 `url:"mode,omitempty" json:"mode,omitempty"`
}
// DiskMigrate - migrate compute's disk to target disk. Source disk will be detached, target disk will be attached to the same PCI slot.
// (WARNING) Current realisation is limited. No actual data migration will be performed.
// Use this API if target disk already connected to compute and you only need to save changes for next reboot (mode: 1).
func (c Compute) DiskMigrate(ctx context.Context, req DiskMigrateRequest) (bool, error) {
err := validators.ValidateRequest(req)
if err != nil {
return false, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/compute/diskMigrate"
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil {
return false, err
}
result, err := strconv.ParseBool(string(res))
if err != nil {
return false, err
}
return result, nil
}

View File

@@ -0,0 +1,46 @@
package compute
import (
"context"
"net/http"
"strconv"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// DiskSwitchToReplicationRequest struct to switch disk to it's replication
type DiskSwitchToReplicationRequest struct {
// ID of compute instance
// Required: true
ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"`
// ID of the disk to switch
// Required: true
DiskID uint64 `url:"diskId" json:"diskId" validate:"required"`
// Delete replication relationship
// Required: false
StopReplication bool `url:"stopReplication" json:"stopReplication"`
}
// DiskSwitchToReplication switches disk to it's replication
func (c Compute) DiskSwitchToReplication(ctx context.Context, req DiskSwitchToReplicationRequest) (bool, error) {
err := validators.ValidateRequest(req)
if err != nil {
return false, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/compute/diskSwitchToReplication"
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil {
return false, err
}
result, err := strconv.ParseBool(string(res))
if err != nil {
return false, err
}
return result, nil
}

View File

@@ -4,6 +4,8 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"net/http" "net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// ListRequest struct to get list of available computes // ListRequest struct to get list of available computes
@@ -52,6 +54,10 @@ type ListRequest struct {
// Required: false // Required: false
IncludeDeleted bool `url:"includedeleted,omitempty" json:"includedeleted,omitempty"` IncludeDeleted bool `url:"includedeleted,omitempty" json:"includedeleted,omitempty"`
// Sort by one of supported fields, format +|-(field)
// Required: false
SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"`
// Page number // Page number
// Required: false // Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"` Page uint64 `url:"page,omitempty" json:"page,omitempty"`
@@ -64,6 +70,7 @@ type ListRequest struct {
// List gets list of the available computes. // List gets list of the available computes.
// Filtering based on status is possible // Filtering based on status is possible
func (c Compute) List(ctx context.Context, req ListRequest) (*ListComputes, error) { func (c Compute) List(ctx context.Context, req ListRequest) (*ListComputes, error) {
res, err := c.ListRaw(ctx, req) res, err := c.ListRaw(ctx, req)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -81,6 +88,11 @@ func (c Compute) List(ctx context.Context, req ListRequest) (*ListComputes, erro
// ListRaw gets list of the available computes. // ListRaw gets list of the available computes.
func (c Compute) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { func (c Compute) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) {
if err := validators.ValidateRequest(req); err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/compute/list" url := "/cloudapi/compute/list"
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req)

View File

@@ -4,6 +4,8 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"net/http" "net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// ListDeletedRequest struct to get deleted computes list // ListDeletedRequest struct to get deleted computes list
@@ -44,6 +46,10 @@ type ListDeletedRequest struct {
// Required: false // Required: false
ExtNetID uint64 `url:"extNetId,omitempty" json:"extNetId,omitempty"` ExtNetID uint64 `url:"extNetId,omitempty" json:"extNetId,omitempty"`
// Sort by one of supported fields, format +|-(field)
// Required: false
SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"`
// Page number // Page number
// Required: false // Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"` Page uint64 `url:"page,omitempty" json:"page,omitempty"`
@@ -55,6 +61,11 @@ type ListDeletedRequest struct {
// ListDeleted gets list all deleted computes // ListDeleted gets list all deleted computes
func (c Compute) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListComputes, error) { func (c Compute) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListComputes, error) {
if err := validators.ValidateRequest(req); err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/compute/listDeleted" url := "/cloudapi/compute/listDeleted"
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req)

View File

@@ -30,6 +30,10 @@ type ListPCIDeviceRequest struct {
// Required: false // Required: false
Status string `url:"status,omitempty" json:"status,omitempty"` Status string `url:"status,omitempty" json:"status,omitempty"`
// Sort by one of supported fields, format +|-(field)
// Required: false
SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"`
// Page number // Page number
// Required: false // Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"` Page uint64 `url:"page,omitempty" json:"page,omitempty"`
@@ -41,6 +45,7 @@ type ListPCIDeviceRequest struct {
// ListPCIDevice gets list PCI device // ListPCIDevice gets list PCI device
func (c Compute) ListPCIDevice(ctx context.Context, req ListPCIDeviceRequest) (*ListPCIDevices, error) { func (c Compute) ListPCIDevice(ctx context.Context, req ListPCIDeviceRequest) (*ListPCIDevices, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err)) return nil, validators.ValidationErrors(validators.GetErrors(err))

View File

@@ -26,6 +26,10 @@ type ListVGPURequest struct {
// Required: false // Required: false
Status string `url:"status,omitempty" json:"status,omitempty"` Status string `url:"status,omitempty" json:"status,omitempty"`
// Sort by one of supported fields, format +|-(field)
// Required: false
SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"`
// Page number // Page number
// Required: false // Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"` Page uint64 `url:"page,omitempty" json:"page,omitempty"`
@@ -41,6 +45,7 @@ type ListVGPURequest struct {
// ListVGPU gets list vGPU // ListVGPU gets list vGPU
func (c Compute) ListVGPU(ctx context.Context, req ListVGPURequest) (*ListVGPUs, error) { func (c Compute) ListVGPU(ctx context.Context, req ListVGPURequest) (*ListVGPUs, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err)) return nil, validators.ValidationErrors(validators.GetErrors(err))

View File

@@ -214,6 +214,9 @@ type RecordNetAttach struct {
// List VNF IDs // List VNF IDs
VNFs []uint64 `json:"vnfs"` VNFs []uint64 `json:"vnfs"`
// Maximum transmission unit
MTU uint64 `json:"mtu"`
} }
// Detailed information about audit // Detailed information about audit
@@ -305,6 +308,12 @@ type RecordCompute struct {
// Boot disk size // Boot disk size
BootDiskSize uint64 `json:"bootdiskSize"` BootDiskSize uint64 `json:"bootdiskSize"`
// CD Image Id
CdImageId uint64 `json:"cdImageId"`
// Chipset
Chipset string `json:"chipset"`
// Clone reference // Clone reference
CloneReference uint64 `json:"cloneReference"` CloneReference uint64 `json:"cloneReference"`
@@ -314,6 +323,9 @@ type RecordCompute struct {
// Compute CI ID // Compute CI ID
ComputeCIID uint64 `json:"computeciId"` ComputeCIID uint64 `json:"computeciId"`
// CPU Pin
CPUPin bool `json:"cpupin"`
// Number of cores // Number of cores
CPU uint64 `json:"cpus"` CPU uint64 `json:"cpus"`
@@ -350,6 +362,9 @@ type RecordCompute struct {
// GUID // GUID
GUID uint64 `json:"guid"` GUID uint64 `json:"guid"`
// HPBacked
HPBacked bool `json:"hpBacked"`
// ID // ID
ID uint64 `json:"id"` ID uint64 `json:"id"`
@@ -380,6 +395,15 @@ type RecordCompute struct {
// Name // Name
Name string `json:"name"` Name string `json:"name"`
// NeedReboot
NeedReboot bool `json:"needReboot"`
// Numa Affinity
NumaAffinity string `json:"numaAffinity"`
//NumaNodeId
NumaNodeId int64 `json:"numaNodeId"`
// Natable VINS ID // Natable VINS ID
NatableVINSID uint64 `json:"natableVinsId"` NatableVINSID uint64 `json:"natableVinsId"`
@@ -413,6 +437,9 @@ type RecordCompute struct {
// Resource name // Resource name
ResName string `json:"resName"` ResName string `json:"resName"`
// Reserved Node Cpus
ReservedNodeCpus []uint64 `json:"reservedNodeCpus"`
// Resource group ID // Resource group ID
RGID uint64 `json:"rgId"` RGID uint64 `json:"rgId"`
@@ -459,6 +486,30 @@ type RecordCompute struct {
VirtualImageName string `json:"virtualImageName"` VirtualImageName string `json:"virtualImageName"`
} }
// Information about libvirt settings
type LibvirtSettings struct {
// TX mode
TXMode string `json:"txmode"`
// IO event
IOEventFD string `json:"ioeventfd"`
// Event ID
EventIDx string `json:"event_idx"`
// Number of queues
Queues uint64 `json:"queues"`
// RX queue size
RXQueueSize uint64 `json:"rx_queue_size"`
// TX queue size
TXQueueSize uint64 `json:"tx_queue_size"`
// GUID
GUID string `json:"guid"`
}
// Main information about OS user // Main information about OS user
type ItemOSUser struct { type ItemOSUser struct {
// GUID // GUID
@@ -497,6 +548,9 @@ type ListSnapSets []ItemSnapSet
// Main information about VNF // Main information about VNF
type ItemVNFInterface struct { type ItemVNFInterface struct {
// Bus number
BusNumber uint64 `json:"bus_number"`
// Connection ID // Connection ID
ConnID uint64 `json:"connId"` ConnID uint64 `json:"connId"`
@@ -521,9 +575,15 @@ type ItemVNFInterface struct {
// Listen SSH or not // Listen SSH or not
ListenSSH bool `json:"listenSsh"` ListenSSH bool `json:"listenSsh"`
// Libvirt Settings
LibvirtSettings LibvirtSettings `json:"libvirtSettings"`
// MAC // MAC
MAC string `json:"mac"` MAC string `json:"mac"`
// Maximum transmission unit
MTU uint64 `json:"mtu"`
// Name // Name
Name string `json:"name"` Name string `json:"name"`
@@ -536,6 +596,9 @@ type ItemVNFInterface struct {
// Network type // Network type
NetType string `json:"netType"` NetType string `json:"netType"`
// NodeID
NodeID int64 `json:"nodeId"`
// PCI slot // PCI slot
PCISlot int64 `json:"pciSlot"` PCISlot int64 `json:"pciSlot"`
@@ -579,6 +642,9 @@ type ItemComputeDisk struct {
// Boot partition // Boot partition
BootPartition uint64 `json:"bootPartition"` BootPartition uint64 `json:"bootPartition"`
// Bus number
BusNumber uint64 `json:"bus_number"`
// Created time // Created time
CreatedTime uint64 `json:"createdTime"` CreatedTime uint64 `json:"createdTime"`
@@ -651,6 +717,9 @@ type ItemComputeDisk struct {
// Reality device number // Reality device number
RealityDeviceNumber uint64 `json:"realityDeviceNumber"` RealityDeviceNumber uint64 `json:"realityDeviceNumber"`
// Replication
Replication ItemReplication `json:"replication"`
// Resource ID // Resource ID
ResID string `json:"resId"` ResID string `json:"resId"`
@@ -685,6 +754,26 @@ type ItemComputeDisk struct {
VMID uint64 `json:"vmid"` VMID uint64 `json:"vmid"`
} }
type ItemReplication struct {
// DiskID
DiskID uint64 `json:"diskId"`
// PoolID
PoolID string `json:"poolId"`
// Role
Role string `json:"role"`
// SelfVolumeID
SelfVolumeID string `json:"selfVolumeId"`
// StorageID
StorageID string `json:"storageId"`
// VolumeID
VolumeID string `json:"volumeId"`
}
// Main information about snapshot extend // Main information about snapshot extend
type SnapshotExtend struct { type SnapshotExtend struct {
// GUID // GUID
@@ -758,6 +847,7 @@ type IOTune struct {
type ItemCompute struct { type ItemCompute struct {
// Access Control List // Access Control List
ACL ListACL `json:"acl"` ACL ListACL `json:"acl"`
// Account ID // Account ID
AccountID uint64 `json:"accountId"` AccountID uint64 `json:"accountId"`
@@ -785,6 +875,12 @@ type ItemCompute struct {
// Boot disk size // Boot disk size
BootDiskSize uint64 `json:"bootdiskSize"` BootDiskSize uint64 `json:"bootdiskSize"`
// CD Image Id
CdImageId uint64 `json:"cdImageId"`
// Chipset
Chipset string `json:"chipset"`
// Clone reference // Clone reference
CloneReference uint64 `json:"cloneReference"` CloneReference uint64 `json:"cloneReference"`
@@ -794,6 +890,9 @@ type ItemCompute struct {
// Compute CI ID // Compute CI ID
ComputeCIID uint64 `json:"computeciId"` ComputeCIID uint64 `json:"computeciId"`
// CPU Pin
CPUPin bool `json:"cpupin"`
// Number of cores // Number of cores
CPU uint64 `json:"cpus"` CPU uint64 `json:"cpus"`
@@ -830,6 +929,9 @@ type ItemCompute struct {
// GUID // GUID
GUID uint64 `json:"guid"` GUID uint64 `json:"guid"`
// HPBacked
HPBacked bool `json:"hpBacked"`
// ID // ID
ID uint64 `json:"id"` ID uint64 `json:"id"`
@@ -857,6 +959,15 @@ type ItemCompute struct {
// Name // Name
Name string `json:"name"` Name string `json:"name"`
// NeedReboot
NeedReboot bool `json:"needReboot"`
// Numa Affinity
NumaAffinity string `json:"numaAffinity"`
//NumaNodeId
NumaNodeId int64 `json:"numaNodeId"`
// Pinned or not // Pinned or not
Pinned bool `json:"pinned"` Pinned bool `json:"pinned"`
@@ -872,6 +983,9 @@ type ItemCompute struct {
// Resource name // Resource name
ResName string `json:"resName"` ResName string `json:"resName"`
// Reserved Node Cpus
ReservedNodeCpus []uint64 `json:"reservedNodeCpus"`
// Resource group ID // Resource group ID
RGID uint64 `json:"rgId"` RGID uint64 `json:"rgId"`
@@ -928,6 +1042,9 @@ type InfoDisk struct {
// PCISlot // PCISlot
PCISlot int64 `json:"pciSlot"` PCISlot int64 `json:"pciSlot"`
// Bus number
BusNumber uint64 `json:"bus_number"`
} }
// List information about computes // List information about computes
@@ -942,16 +1059,106 @@ type ListComputes struct {
// List VGPUs // List VGPUs
type ListVGPUs struct { type ListVGPUs struct {
// Data // Data
Data []interface{} `json:"data"` Data []ItemVGPU `json:"data"`
// Entry count // Entry count
EntryCount uint64 `json:"entryCount"` EntryCount uint64 `json:"entryCount"`
} }
// Main information about vgpu device
type ItemVGPU struct {
// Account ID
AccountID uint64 `json:"accountId"`
// Created Time
CreatedTime uint64 `json:"createdTime"`
// Deleted Time
DeletedTime uint64 `json:"deletedTime"`
// GID
GID uint64 `json:"gid"`
// GUID
GUID uint64 `json:"guid"`
// ID
ID uint64 `json:"id"`
// Last Claimed By
LastClaimedBy uint64 `json:"lastClaimedBy"`
// Last Update Time
LastUpdateTime uint64 `json:"lastUpdateTime"`
// Mode
Mode string `json:"mode"`
// PCI Slot
PCISlot uint64 `json:"pciSlot"`
// PGPUID
PGPUID uint64 `json:"pgpuid"`
// Profile ID
ProfileID uint64 `json:"profileId"`
// RAM
RAM uint64 `json:"ram"`
// Reference ID
ReferenceID string `json:"referenceId"`
// RG ID
RGID uint64 `json:"rgId"`
// Status
Status string `json:"status"`
// Type
Type string `json:"type"`
// VM ID
VMID uint64 `json:"vmid"`
}
// Main information about PCI device
type ItemPCIDevice struct {
// Compute ID
ComputeID uint64 `json:"computeId"`
// Description
Description string `json:"description"`
// GUID
GUID uint64 `json:"guid"`
// HwPath
HwPath string `json:"hwPath"`
// ID
ID uint64 `json:"id"`
// Name
Name string `json:"name"`
// Resource group ID
RGID uint64 `json:"rgId"`
// Stack ID
StackID uint64 `json:"stackId"`
// Status
Status string `json:"status"`
// System name
SystemName string `json:"systemName"`
}
// List PCI devices // List PCI devices
type ListPCIDevices struct { type ListPCIDevices struct {
// Data // Data
Data []interface{} `json:"data"` Data []ItemPCIDevice `json:"data"`
// Entry count // Entry count
EntryCount uint64 `json:"entryCount"` EntryCount uint64 `json:"entryCount"`

View File

@@ -16,9 +16,11 @@ type NetAttachRequest struct {
// Network type // Network type
// 'EXTNET' for connect to external network directly // 'EXTNET' for connect to external network directly
// and 'VINS' for connect to ViNS // 'VINS' for connect to ViNS
// 'VFNIC' for connect to vfpool
// 'DPDK' for connect to DPDK
// Required: true // Required: true
NetType string `url:"netType" json:"netType" validate:"computeNetType"` NetType string `url:"netType" json:"netType" validate:"computex86NetType"`
// Network ID for connect to // Network ID for connect to
// For EXTNET - external network ID // For EXTNET - external network ID
@@ -29,6 +31,10 @@ type NetAttachRequest struct {
// Directly required IP address for new network interface // Directly required IP address for new network interface
// Required: false // Required: false
IPAddr string `url:"ipAddr,omitempty" json:"ipAddr,omitempty"` IPAddr string `url:"ipAddr,omitempty" json:"ipAddr,omitempty"`
// Used only for DPDK type, must be 1-9216
// Required: false
MTU uint64 `url:"mtu,omitempty" json:"mtu,omitempty" validate:"omitempty,mtu"`
} }
// NetAttach attaches network to compute and gets info about network // NetAttach attaches network to compute and gets info about network

View File

@@ -24,8 +24,8 @@ type PFWAddRequest struct {
PublicPortEnd int64 `url:"publicPortEnd,omitempty" json:"publicPortEnd,omitempty"` PublicPortEnd int64 `url:"publicPortEnd,omitempty" json:"publicPortEnd,omitempty"`
// Internal base port number // Internal base port number
// Required: true // Required: false
LocalBasePort uint64 `url:"localBasePort" json:"localBasePort" validate:"required"` LocalBasePort uint64 `url:"localBasePort,omitempty" json:"localBasePort,omitempty"`
// Network protocol // Network protocol
// either "tcp" or "udp" // either "tcp" or "udp"

View File

@@ -29,6 +29,16 @@ type ResizeRequest struct {
Force bool `url:"force,omitempty" json:"force,omitempty"` Force bool `url:"force,omitempty" json:"force,omitempty"`
} }
// GetRAM returns RAM field values
func (r ResizeRequest) GetRAM() map[string]uint64 {
res := make(map[string]uint64, 1)
res["RAM"] = r.RAM
return res
}
// Resize resizes compute instance // Resize resizes compute instance
func (c Compute) Resize(ctx context.Context, req ResizeRequest) (bool, error) { func (c Compute) Resize(ctx context.Context, req ResizeRequest) (bool, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)

View File

@@ -3,7 +3,6 @@ package compute
import ( import (
"context" "context"
"net/http" "net/http"
"strconv"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
@@ -19,24 +18,52 @@ type SnapshotDeleteRequest struct {
Label string `url:"label" json:"label" validate:"required"` Label string `url:"label" json:"label" validate:"required"`
} }
type wrapperSnapshotDeleteRequeststruct struct {
SnapshotDeleteRequest
AsyncMode bool `url:"asyncMode"`
}
// SnapshotDelete deletes specified compute snapshot // SnapshotDelete deletes specified compute snapshot
func (c Compute) SnapshotDelete(ctx context.Context, req SnapshotDeleteRequest) (bool, error) { func (c Compute) SnapshotDelete(ctx context.Context, req SnapshotDeleteRequest) (string, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
return false, validators.ValidationErrors(validators.GetErrors(err)) return "", validators.ValidationErrors(validators.GetErrors(err))
}
reqWrapped := wrapperSnapshotDeleteRequeststruct{
SnapshotDeleteRequest: req,
AsyncMode: false,
} }
url := "/cloudapi/compute/snapshotDelete" url := "/cloudapi/compute/snapshotDelete"
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped)
if err != nil { if err != nil {
return false, err return "", err
} }
result, err := strconv.ParseBool(string(res)) return string(res), nil
if err != nil { }
return false, err
} // SnapshotDeleteAsync deletes specified compute snapshot
func (c Compute) SnapshotDeleteAsync(ctx context.Context, req SnapshotDeleteRequest) (string, error) {
return result, nil err := validators.ValidateRequest(req)
if err != nil {
return "", validators.ValidationErrors(validators.GetErrors(err))
}
reqWrapped := wrapperSnapshotDeleteRequeststruct{
SnapshotDeleteRequest: req,
AsyncMode: true,
}
url := "/cloudapi/compute/snapshotDelete"
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped)
if err != nil {
return "", err
}
return string(res), nil
} }

View File

@@ -21,6 +21,28 @@ type UpdateRequest struct {
// New description // New description
// Required: false // Required: false
Description string `url:"desc,omitempty" json:"desc,omitempty"` Description string `url:"desc,omitempty" json:"desc,omitempty"`
// Rule for VM placement with NUMA affinity.
// Possible values - none (placement without NUMA affinity),
// strict (strictly with NUMA affinity, if not possible - do not start VM),
// loose (use NUMA affinity if possible)
// Required: false
// Default: none
NumaAffinity string `url:"numaAffinity,omitempty" json:"numaAffinity,omitempty" validate:"omitempty,numaAffinity"`
// Run VM on dedicated CPUs. To use this feature, the system must be pre-configured by allocating CPUs on the physical node
// Required: false
// Default: false
CPUPin bool `url:"cpupin" json:"cpupin"`
// Type of the emulated system, Q35 or i440fx
// Required: false
Chipset string `url:"chipset,omitempty" json:"chipset,omitempty"`
// Use Huge Pages to allocate RAM of the virtual machine. The system must be pre-configured by allocating Huge Pages on the physical node
// Required: false
// Default: false
HPBacked bool `url:"hpBacked" json:"hpBacked"`
} }
// Update updates some properties of the compute // Update updates some properties of the compute

View File

@@ -21,10 +21,6 @@ type DeleteRequest struct {
// Whether to completely delete the disk, works only with non attached disks // Whether to completely delete the disk, works only with non attached disks
// Required: false // Required: false
Permanently bool `url:"permanently,omitempty" json:"permanently,omitempty"` Permanently bool `url:"permanently,omitempty" json:"permanently,omitempty"`
// Reason to delete
// Required: false
Reason string `url:"reason,omitempty" json:"reason,omitempty"`
} }
// Delete deletes disk by ID // Delete deletes disk by ID

View File

@@ -14,10 +14,6 @@ type DisksDeleteRequest struct {
// Required: true // Required: true
DisksIDs []uint64 `url:"diskIds" json:"diskIds" validate:"required"` DisksIDs []uint64 `url:"diskIds" json:"diskIds" validate:"required"`
// Reason for deleting the disks
// Required: true
Reason string `url:"reason" json:"reason" validate:"required"`
// Whether to completely delete the disks, works only with non attached disks // Whether to completely delete the disks, works only with non attached disks
// Required: false // Required: false
Permanently bool `url:"permanently,omitempty" json:"permanently,omitempty"` Permanently bool `url:"permanently,omitempty" json:"permanently,omitempty"`

View File

@@ -0,0 +1,126 @@
package disks
import (
"context"
"net/http"
"strconv"
"strings"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// FromPlatformDiskRequest struct to create template from platform disk
type FromPlatformDiskRequest struct {
// ID of the disk
// Required: true
DiskID uint64 `url:"diskId" json:"diskId" validate:"required"`
// Name of the rescue disk
// Required: true
Name string `url:"name" json:"name" validate:"required"`
// Boot type of image BIOS or UEFI
// Required: true
BootType string `url:"boottype" json:"boottype" validate:"imageBootType"`
// Image type linux, windows or other
// Required: true
ImageType string `url:"imagetype" json:"imagetype" validate:"imageType"`
// Binary architecture of this image
// Should be:
// - X86_64
// Required: true
Architecture string `url:"architecture" json:"architecture" validate:"imageArchitecture"`
// Username for the image
// Required: false
Username string `url:"username,omitempty" json:"username,omitempty"`
// Password for the image
// Required: false
Password string `url:"password,omitempty" json:"password,omitempty"`
// Account ID to make the image exclusive
// Required: false
AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"`
// SEP ID
// Required: false
SepID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"`
// Pool for image create
// Required: false
PoolName string `url:"poolName,omitempty" json:"poolName,omitempty"`
// List of types of compute suitable for image
// Example: [ "KVM_X86" ]
// Required: true
Drivers []string `url:"drivers" json:"drivers" validate:"required"`
// Does this machine supports hot resize
// Required: false
HotResize bool `url:"hotresize" json:"hotresize"`
// Bootable image
// Required: true
Bootable bool `url:"bootable" json:"bootable"`
}
type wrapperFromPlatformDiskRequest struct {
FromPlatformDiskRequest
AsyncMode bool `url:"asyncMode"`
}
// FromPlatformDisk creates template from platform disk in sync mode.
// It returns id of created disk and error.
func (d Disks) FromPlatformDisk(ctx context.Context, req FromPlatformDiskRequest) (uint64, error) {
err := validators.ValidateRequest(req)
if err != nil {
return 0, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/disks/fromPlatformDisk"
reqWrapped := wrapperFromPlatformDiskRequest{
FromPlatformDiskRequest: req,
AsyncMode: false,
}
res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped)
if err != nil {
return 0, err
}
result, err := strconv.ParseUint(string(res), 10, 64)
if err != nil {
return 0, err
}
return result, nil
}
// FromPlatformDiskAsync creates template from platform disk in async mode.
// It returns guid of task and error.
func (d Disks) FromPlatformDiskAsync(ctx context.Context, req FromPlatformDiskRequest) (string, error) {
err := validators.ValidateRequest(req)
if err != nil {
return "", validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/disks/fromPlatformDisk"
reqWrapped := wrapperFromPlatformDiskRequest{
FromPlatformDiskRequest: req,
AsyncMode: true,
}
res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped)
if err != nil {
return "", err
}
result := strings.ReplaceAll(string(res), "\"", "")
return result, nil
}

View File

@@ -4,6 +4,8 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"net/http" "net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// ListRequest struct to get list of disks // ListRequest struct to get list of disks
@@ -30,7 +32,7 @@ type ListRequest struct {
// Find by shared, true or false // Find by shared, true or false
// Required: false // Required: false
Shared bool `url:"shared,omitempty" json:"shared,omitempty"` Shared interface{} `url:"shared,omitempty" json:"shared,omitempty" validate:"omitempty,isBool"`
// ID of the account the disks belong to // ID of the account the disks belong to
// Required: false // Required: false
@@ -48,6 +50,10 @@ type ListRequest struct {
// Required: false // Required: false
Pool string `url:"pool,omitempty" json:"pool,omitempty"` Pool string `url:"pool,omitempty" json:"pool,omitempty"`
// Sort by one of supported fields, format +|-(field)
// Required: false
SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"`
// Page number // Page number
// Required: false // Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"` Page uint64 `url:"page,omitempty" json:"page,omitempty"`
@@ -59,6 +65,7 @@ type ListRequest struct {
// List gets list of the created disks belonging to an account as a ListDisks struct // List gets list of the created disks belonging to an account as a ListDisks struct
func (d Disks) List(ctx context.Context, req ListRequest) (*ListDisks, error) { func (d Disks) List(ctx context.Context, req ListRequest) (*ListDisks, error) {
res, err := d.ListRaw(ctx, req) res, err := d.ListRaw(ctx, req)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -76,6 +83,11 @@ func (d Disks) List(ctx context.Context, req ListRequest) (*ListDisks, error) {
// ListRaw gets list of the created disks belonging to an account as an array of bytes // ListRaw gets list of the created disks belonging to an account as an array of bytes
func (d Disks) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { func (d Disks) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) {
if err := validators.ValidateRequest(req); err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/disks/list" url := "/cloudapi/disks/list"
res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req)

View File

@@ -4,6 +4,8 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"net/http" "net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// ListDeletedRequest struct to get list of deleted disks // ListDeletedRequest struct to get list of deleted disks
@@ -26,7 +28,7 @@ type ListDeletedRequest struct {
// Find by shared, true or false // Find by shared, true or false
// Required: false // Required: false
Shared bool `url:"shared,omitempty" json:"shared,omitempty"` Shared interface{} `url:"shared,omitempty" json:"shared,omitempty" validate:"omitempty,isBool"`
// ID of the account the disks belong to // ID of the account the disks belong to
// Required: false // Required: false
@@ -36,6 +38,10 @@ type ListDeletedRequest struct {
// Required: false // Required: false
Type string `url:"type,omitempty" json:"type,omitempty"` Type string `url:"type,omitempty" json:"type,omitempty"`
// Sort by one of supported fields, format +|-(field)
// Required: false
SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"`
// Page number // Page number
// Required: false // Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"` Page uint64 `url:"page,omitempty" json:"page,omitempty"`
@@ -47,6 +53,11 @@ type ListDeletedRequest struct {
// ListDeleted gets list the deleted disks belonging to an account // ListDeleted gets list the deleted disks belonging to an account
func (d Disks) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListDisks, error) { func (d Disks) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListDisks, error) {
if err := validators.ValidateRequest(req); err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/disks/listDeleted" url := "/cloudapi/disks/listDeleted"
res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req)

View File

@@ -4,13 +4,19 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"net/http" "net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// ListTypesRequest struct to get list types of disks // ListTypesRequest struct to get list types of disks
type ListTypesRequest struct { type ListTypesRequest struct {
// Show detailed disk types by seps // Show detailed disk types by seps
// Required: true // Required: true
Detailed bool `url:"detailed" json:"detailed" validate:"required"` Detailed bool `url:"detailed" json:"detailed"`
// Sort by one of supported fields, format +|-(field)
// Required: false
SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"`
// Page number // Page number
// Required: false // Required: false
@@ -23,6 +29,11 @@ type ListTypesRequest struct {
// ListTypes gets list defined disk types // ListTypes gets list defined disk types
func (d Disks) ListTypes(ctx context.Context, req ListTypesRequest) (*ListTypes, error) { func (d Disks) ListTypes(ctx context.Context, req ListTypesRequest) (*ListTypes, error) {
if err := validators.ValidateRequest(req); err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/disks/listTypes" url := "/cloudapi/disks/listTypes"
res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req)

View File

@@ -4,6 +4,8 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"net/http" "net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// ListUnattachedRequest struct to get list of unattached disk // ListUnattachedRequest struct to get list of unattached disk
@@ -40,6 +42,10 @@ type ListUnattachedRequest struct {
// Required: false // Required: false
Pool string `url:"pool,omitempty" json:"pool,omitempty"` Pool string `url:"pool,omitempty" json:"pool,omitempty"`
// Sort by one of supported fields, format +|-(field)
// Required: false
SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"`
// Page number // Page number
// Required: false // Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"` Page uint64 `url:"page,omitempty" json:"page,omitempty"`
@@ -51,6 +57,11 @@ type ListUnattachedRequest struct {
// ListUnattached gets list of unattached disks // ListUnattached gets list of unattached disks
func (d Disks) ListUnattached(ctx context.Context, req ListUnattachedRequest) (*ListDisksUnattached, error) { func (d Disks) ListUnattached(ctx context.Context, req ListUnattachedRequest) (*ListDisksUnattached, error) {
if err := validators.ValidateRequest(req); err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/disks/listUnattached" url := "/cloudapi/disks/listUnattached"
res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req)

View File

@@ -74,6 +74,9 @@ type ItemDisk struct {
// Purge time // Purge time
PurgeTime uint64 `json:"purgeTime"` PurgeTime uint64 `json:"purgeTime"`
// Replication
Replication ItemReplication `json:"replication"`
// Resource ID // Resource ID
ResID string `json:"resId"` ResID string `json:"resId"`
@@ -262,8 +265,10 @@ type ListDisks struct {
// List of unattached disks // List of unattached disks
type ListDisksUnattached struct { type ListDisksUnattached struct {
// Data
Data []ItemDiskUnattached `json:"data"` Data []ItemDiskUnattached `json:"data"`
// Entry count
EntryCount uint64 `json:"entryCount"` EntryCount uint64 `json:"entryCount"`
} }
@@ -403,6 +408,9 @@ type RecordDisk struct {
// Purge time // Purge time
PurgeTime uint64 `json:"purgeTime"` PurgeTime uint64 `json:"purgeTime"`
// Replication
Replication ItemReplication `json:"replication"`
// Resource ID // Resource ID
ResID string `json:"resId"` ResID string `json:"resId"`
@@ -443,6 +451,26 @@ type RecordDisk struct {
VMID uint64 `json:"vmid"` VMID uint64 `json:"vmid"`
} }
type ItemReplication struct {
// DiskID
DiskID uint64 `json:"diskId"`
// PoolID
PoolID string `json:"poolId"`
// Role
Role string `json:"role"`
// SelfVolumeID
SelfVolumeID string `json:"selfVolumeId"`
// StorageID
StorageID string `json:"storageId"`
// VolumeID
VolumeID string `json:"volumeId"`
}
type ListTypes struct { type ListTypes struct {
// Data // Data
Data []interface{} `json:"data"` Data []interface{} `json:"data"`

View File

@@ -0,0 +1,52 @@
package disks
import (
"context"
"net/http"
"strconv"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// ReplicateRequest struct to create an empty disk in chosen SEP and pool combination.
type ReplicateRequest struct {
// Id of the disk to replicate. This disk will become master in replication
// Required: true
DiskID uint64 `url:"diskId" json:"diskId" validate:"required"`
// Name of replica disk to create
// Required: true
Name string `url:"name" json:"name" validate:"required"`
// ID of SEP to create slave disk
// Required: true
SepID uint64 `url:"sepId" json:"sepId" validate:"required"`
// Pool name to create slave disk in
// Required: true
PoolName string `url:"poolName" json:"poolName" validate:"required"`
}
// Create an empty disk in chosen SEP and pool combination.
// Starts replication between chosen disk and newly created disk
// Note: only TATLIN type SEP are supported for replications between
func (d Disks) Replicate(ctx context.Context, req ReplicateRequest) (uint64, error) {
err := validators.ValidateRequest(req)
if err != nil {
return 0, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/disks/replicate"
res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil {
return 0, err
}
result, err := strconv.ParseUint(string(res), 10, 64)
if err != nil {
return 0, err
}
return result, nil
}

View File

@@ -0,0 +1,38 @@
package disks
import (
"context"
"net/http"
"strconv"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// ReplicationResume struct to resume suspended replication
type ReplicationResumeRequest struct {
// Id of the disk
// Required: true
DiskID uint64 `url:"diskId" json:"diskId" validate:"required"`
}
// ReplicationResume resume suspended replication
func (d Disks) ReplicationResume(ctx context.Context, req ReplicationResumeRequest) (bool, error) {
err := validators.ValidateRequest(req)
if err != nil {
return false, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/disks/replicationResume"
res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil {
return false, err
}
result, err := strconv.ParseBool(string(res))
if err != nil {
return false, err
}
return result, nil
}

View File

@@ -0,0 +1,38 @@
package disks
import (
"context"
"net/http"
"strconv"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// ReplicationReverseRequest struct to change role between disks replications
type ReplicationReverseRequest struct {
// Id of the disk
// Required: true
DiskID uint64 `url:"diskId" json:"diskId" validate:"required"`
}
// ReplicationReverse change role between disks replications
func (d Disks) ReplicationReverse(ctx context.Context, req ReplicationReverseRequest) (bool, error) {
err := validators.ValidateRequest(req)
if err != nil {
return false, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/disks/replicationReverse"
res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil {
return false, err
}
result, err := strconv.ParseBool(string(res))
if err != nil {
return false, err
}
return result, nil
}

View File

@@ -0,0 +1,43 @@
package disks
import (
"context"
"net/http"
"strconv"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// ReplicationStartRequest struct to starts replication between two chosen disks
type ReplicationStartRequest struct {
// Id of the disk to replicate. Primary disk in replication
// Required: true
DiskID uint64 `url:"diskId" json:"diskId" validate:"required"`
// ID of target disk. Secondary disk in replication
// Required: true
TargetDiskID uint64 `url:"targetDiskId" json:"targetDiskId" validate:"required"`
}
// ReplicationStart starts replication between two chosen disks. It's required for both disks to have same size to avoid replication conflicts
// Note: Source disk's SEP and target SEP supported only of TATLIN type.
func (d Disks) ReplicationStart(ctx context.Context, req ReplicationStartRequest) (bool, error) {
err := validators.ValidateRequest(req)
if err != nil {
return false, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/disks/replicationStart"
res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil {
return false, err
}
result, err := strconv.ParseBool(string(res))
if err != nil {
return false, err
}
return result, nil
}

View File

@@ -0,0 +1,32 @@
package disks
import (
"context"
"net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// ReplicationStatusRequest struct to get replication status
type ReplicationStatusRequest struct {
// Id of the disk
// Required: true
DiskID uint64 `url:"diskId" json:"diskId" validate:"required"`
}
// ReplicationStatus get replication status
func (d Disks) ReplicationStatus(ctx context.Context, req ReplicationStatusRequest) (string, error) {
err := validators.ValidateRequest(req)
if err != nil {
return "", validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/disks/replicationStatus"
res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil {
return "", err
}
return string(res), nil
}

View File

@@ -0,0 +1,38 @@
package disks
import (
"context"
"net/http"
"strconv"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// ReplicationStopRequest struct to remove replication between disks completely
type ReplicationStopRequest struct {
// Id of the disk
// Required: true
DiskID uint64 `url:"diskId" json:"diskId" validate:"required"`
}
// ReplicationStop remove replication between disks completely
func (d Disks) ReplicationStop(ctx context.Context, req ReplicationStopRequest) (bool, error) {
err := validators.ValidateRequest(req)
if err != nil {
return false, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/disks/replicationStop"
res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil {
return false, err
}
result, err := strconv.ParseBool(string(res))
if err != nil {
return false, err
}
return result, nil
}

View File

@@ -0,0 +1,38 @@
package disks
import (
"context"
"net/http"
"strconv"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// ReplicationSuspendRequest struct to pause replication with possibility to resume from pause moment
type ReplicationSuspendRequest struct {
// Id of the disk
// Required: true
DiskID uint64 `url:"diskId" json:"diskId" validate:"required"`
}
// ReplicationSuspend pause replication with possibility to resume from pause moment
func (d Disks) ReplicationSuspend(ctx context.Context, req ReplicationSuspendRequest) (bool, error) {
err := validators.ValidateRequest(req)
if err != nil {
return false, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/disks/replicationSuspend"
res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil {
return false, err
}
result, err := strconv.ParseBool(string(res))
if err != nil {
return false, err
}
return result, nil
}

View File

@@ -13,10 +13,6 @@ type RestoreRequest struct {
// ID of the disk to restore // ID of the disk to restore
// Required: true // Required: true
DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` DiskID uint64 `url:"diskId" json:"diskId" validate:"required"`
// Reason for restoring the disk
// Required: true
Reason string `url:"reason" json:"reason" validate:"required"`
} }
// Restore restores a deleted unattached disk from recycle bin // Restore restores a deleted unattached disk from recycle bin

View File

@@ -18,6 +18,14 @@ type SearchRequest struct {
// If false, then disks having one of the statuses are not listed // If false, then disks having one of the statuses are not listed
// Required: false // Required: false
ShowAll bool `url:"show_all,omitempty" json:"show_all,omitempty"` ShowAll bool `url:"show_all,omitempty" json:"show_all,omitempty"`
// Page number
// Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"`
// Page size
// Required: false
Size uint64 `url:"size,omitempty" json:"size,omitempty"`
} }
// Search searches disks // Search searches disks

8
pkg/cloudapi/dpdknet.go Normal file
View File

@@ -0,0 +1,8 @@
package cloudapi
import "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/dpdknet"
// Accessing the DPDKNet method group
func (ca *CloudAPI) DPDKNet() *dpdknet.DPDKNet {
return dpdknet.New(ca.client)
}

View File

@@ -0,0 +1,15 @@
package dpdknet
import "repository.basistech.ru/BASIS/decort-golang-sdk/interfaces"
// Structure for creating request to DPDK network
type DPDKNet struct {
client interfaces.Caller
}
// Builder for dpdk endpoints
func New(client interfaces.Caller) *DPDKNet {
return &DPDKNet{
client,
}
}

View File

@@ -0,0 +1,46 @@
package dpdknet
import (
"context"
"encoding/json"
"net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// GetRequest struct to get information about DPDK network
type GetRequest struct {
// ID of the DPDK network
// Required: true
DPDKID uint64 `url:"dpdkId" json:"dpdkId" validate:"required"`
}
// Get DPDK network details as a RecordDPDKNet struct
func (d DPDKNet) Get(ctx context.Context, req GetRequest) (*RecordDPDKNet, error) {
res, err := d.GetRaw(ctx, req)
if err != nil {
return nil, err
}
info := RecordDPDKNet{}
err = json.Unmarshal(res, &info)
if err != nil {
return nil, err
}
return &info, nil
}
// GetRaw gets DPDK network details as an array of bytes
func (d DPDKNet) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) {
err := validators.ValidateRequest(req)
if err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/dpdknet/get"
res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req)
return res, err
}

View File

@@ -0,0 +1,79 @@
package dpdknet
import (
"context"
"encoding/json"
"net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// ListRequest struct to get list of DPDK networks
type ListRequest struct {
// Find by id
// Required: false
ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"`
// Find by gid
// Required: false
GID uint64 `url:"gid,omitempty" json:"gid,omitempty"`
// Find by name
// Required: false
Name string `url:"name,omitempty" json:"name,omitempty"`
// Find by description
// Required: false
Description string `url:"description,omitempty" json:"description,omitempty"`
// Find by status
// Required: false
Status string `url:"status,omitempty" json:"status,omitempty"`
// Find by computeIDs
// Required: false
ComputeIDs []uint64 `url:"computeIds,omitempty" json:"computeIds,omitempty"`
// Sort by one of supported fields, format +|-(field)
// Required: false
SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"`
// Page number
// Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"`
// Page size
// Required: false
Size uint64 `url:"size,omitempty" json:"size,omitempty"`
}
// List gets list of the created DPDK networks belonging to an account as a ListDPDKNet struct
func (d DPDKNet) List(ctx context.Context, req ListRequest) (*ListDPDKNet, error) {
res, err := d.ListRaw(ctx, req)
if err != nil {
return nil, err
}
list := ListDPDKNet{}
err = json.Unmarshal(res, &list)
if err != nil {
return nil, err
}
return &list, nil
}
// ListRaw gets list of the created DPDK networks belonging to an account as an array of bytes
func (d DPDKNet) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) {
if err := validators.ValidateRequest(req); err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/dpdknet/list"
res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req)
return res, err
}

View File

@@ -0,0 +1,92 @@
package dpdknet
// Detailed information about DPDK network
type RecordDPDKNet struct {
// List of accounts with access
AccountAccess []uint64 `json:"accountAccess"`
// Created time
CreatedTime uint64 `json:"createdTime"`
// Updated time
UpdatedTime uint64 `json:"updatedTime"`
// Description
Description string `json:"description"`
// Grid ID
GID uint64 `json:"gid"`
// Guid ID
GUID uint64 `json:"guid"`
// ID
ID uint64 `json:"id"`
// Name
Name string `json:"name"`
// List of resource groups with access
RGAccess []uint64 `json:"rgAccess"`
// Status
Status string `json:"status"`
// OVS bridge
OVSBridge string `json:"ovsBridge"`
// Vlan ID
VlanID uint64 `json:"vlanId"`
// Compute IDs
ComputeIDs []uint64 `json:"computeIds"`
}
type ListDPDKNet struct {
// Data
Data []ItemDPDKNet `json:"data"`
// Entry count
EntryCount uint64 `json:"entryCount"`
}
type ItemDPDKNet struct {
// List of accounts with access
AccountAccess []uint64 `json:"accountAccess"`
// Created time
CreatedTime uint64 `json:"createdTime"`
// Updated time
UpdatedTime uint64 `json:"updatedTime"`
// Description
Description string `json:"description"`
// Grid ID
GID uint64 `json:"gid"`
// Guid ID
GUID uint64 `json:"guid"`
// ID
ID uint64 `json:"id"`
// Name
Name string `json:"name"`
// List of resource groups with access
RGAccess []uint64 `json:"rgAccess"`
// Status
Status string `json:"status"`
// OVS bridge
OVSBridge string `json:"ovsBridge"`
// Vlan ID
VlanID uint64 `json:"vlanId"`
// Compute IDs
ComputeIDs []uint64 `json:"computeIds"`
}

View File

@@ -4,6 +4,8 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"net/http" "net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// ListRequest struct to get list of external network // ListRequest struct to get list of external network
@@ -36,6 +38,10 @@ type ListRequest struct {
// Required: false // Required: false
Status string `url:"status,omitempty" json:"status,omitempty"` Status string `url:"status,omitempty" json:"status,omitempty"`
// Sort by one of supported fields, format +|-(field)
// Required: false
SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"`
// Page number // Page number
// Required: false // Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"` Page uint64 `url:"page,omitempty" json:"page,omitempty"`
@@ -47,6 +53,7 @@ type ListRequest struct {
// List gets list of all available external networks as a ListExtNets struct // List gets list of all available external networks as a ListExtNets struct
func (e ExtNet) List(ctx context.Context, req ListRequest) (*ListExtNets, error) { func (e ExtNet) List(ctx context.Context, req ListRequest) (*ListExtNets, error) {
res, err := e.ListRaw(ctx, req) res, err := e.ListRaw(ctx, req)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -64,6 +71,11 @@ func (e ExtNet) List(ctx context.Context, req ListRequest) (*ListExtNets, error)
// ListRaw gets list of all available external networks as an array of bytes // ListRaw gets list of all available external networks as an array of bytes
func (e ExtNet) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { func (e ExtNet) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) {
if err := validators.ValidateRequest(req); err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/extnet/list" url := "/cloudapi/extnet/list"
res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req) res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req)

View File

@@ -22,6 +22,10 @@ type ListComputesRequest struct {
// Required: false // Required: false
ComputeID uint64 `url:"computeId,omitempty" json:"computeId,omitempty"` ComputeID uint64 `url:"computeId,omitempty" json:"computeId,omitempty"`
// Sort by one of supported fields, format +|-(field)
// Required: false
SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"`
// Page number // Page number
// Required: false // Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"` Page uint64 `url:"page,omitempty" json:"page,omitempty"`
@@ -33,6 +37,7 @@ type ListComputesRequest struct {
// ListComputes gets computes from account with extnets // ListComputes gets computes from account with extnets
func (e ExtNet) ListComputes(ctx context.Context, req ListComputesRequest) (*ListExtNetComputes, error) { func (e ExtNet) ListComputes(ctx context.Context, req ListComputesRequest) (*ListExtNetComputes, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err)) return nil, validators.ValidationErrors(validators.GetErrors(err))

View File

@@ -170,7 +170,7 @@ type RecordExtNet struct {
Excluded []Excluded `json:"excluded"` Excluded []Excluded `json:"excluded"`
// Free IPs // Free IPs
FreeIPs uint64 `json:"free_ips"` FreeIPs int64 `json:"free_ips"`
// Gateway // Gateway
Gateway string `json:"gateway"` Gateway string `json:"gateway"`

View File

@@ -4,6 +4,8 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"net/http" "net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// ListRequest struct to get list of FLIPGroup available to the current user // ListRequest struct to get list of FLIPGroup available to the current user
@@ -28,6 +30,10 @@ type ListRequest struct {
// Required: false // Required: false
ByIP string `url:"byIp,omitempty" json:"byIp,omitempty"` ByIP string `url:"byIp,omitempty" json:"byIp,omitempty"`
// Find by accountId
// Required: false
AccountId uint64 `url:"accountId,omitempty" json:"accountId,omitempty"`
// Find by resource group ID // Find by resource group ID
// Required: false // Required: false
RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"`
@@ -36,6 +42,10 @@ type ListRequest struct {
// Required: false // Required: false
ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"` ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"`
// Find by list of clientIds
// Required: false
ClientIDs []string `url:"clientIds,omitempty" json:"clientIds,omitempty"`
// Page number // Page number
// Required: false // Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"` Page uint64 `url:"page,omitempty" json:"page,omitempty"`
@@ -43,10 +53,23 @@ type ListRequest struct {
// Page size // Page size
// Required: false // Required: false
Size uint64 `url:"size,omitempty" json:"size,omitempty"` Size uint64 `url:"size,omitempty" json:"size,omitempty"`
// Sort by one of supported fields, format +|-(field)
// Required: false
SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"`
// Find by connId
// Required: false
ConnId uint64 `url:"connId,omitempty" json:"connId,omitempty"`
// Find by status
// Required: false
Status string `url:"status,omitempty" json:"status,omitempty"`
} }
// List gets list of FLIPGroup managed cluster instances available to the current user as a ListFLIPGroups struct // List gets list of FLIPGroup managed cluster instances available to the current user as a ListFLIPGroups struct
func (f FLIPGroup) List(ctx context.Context, req ListRequest) (*ListFLIPGroups, error) { func (f FLIPGroup) List(ctx context.Context, req ListRequest) (*ListFLIPGroups, error) {
res, err := f.ListRaw(ctx, req) res, err := f.ListRaw(ctx, req)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -64,6 +87,11 @@ func (f FLIPGroup) List(ctx context.Context, req ListRequest) (*ListFLIPGroups,
// ListRaw gets list of FLIPGroup managed cluster instances available to the current user as an array of bytes // ListRaw gets list of FLIPGroup managed cluster instances available to the current user as an array of bytes
func (f FLIPGroup) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { func (f FLIPGroup) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) {
if err := validators.ValidateRequest(req); err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/flipgroup/list" url := "/cloudapi/flipgroup/list"
res, err := f.client.DecortApiCall(ctx, http.MethodPost, url, req) res, err := f.client.DecortApiCall(ctx, http.MethodPost, url, req)

View File

@@ -18,10 +18,6 @@ type CreateRequest struct {
// Required: true // Required: true
URL string `url:"url" json:"url" validate:"required,url"` URL string `url:"url" json:"url" validate:"required,url"`
// Grid (platform) ID where this template should be create in
// Required: true
GID uint64 `url:"gid" json:"gid" validate:"required"`
// Boot type of image bios or UEFI // Boot type of image bios or UEFI
// Required: true // Required: true
BootType string `url:"boottype" json:"boottype" validate:"required,imageBootType"` BootType string `url:"boottype" json:"boottype" validate:"required,imageBootType"`
@@ -34,6 +30,17 @@ type CreateRequest struct {
// Required: true // Required: true
ImageType string `url:"imagetype" json:"imagetype" validate:"required,imageType"` ImageType string `url:"imagetype" json:"imagetype" validate:"required,imageType"`
// Account ID to make the image exclusive
// Required: true
AccountID uint64 `url:"accountId" json:"accountId" validate:"required"`
// Select a network interface naming pattern for your Linux machine. eth - onboard, ens - pci slot naming
// Should be:
// - eth
// - ens (default value)
// Required: false
NetworkInterfaceNaming string `url:"networkInterfaceNaming,omitempty" json:"networkInterfaceNaming,omitempty" validate:"omitempty,networkInterfaceNaming"`
// Does this machine supports hot resize // Does this machine supports hot resize
// Required: false // Required: false
HotResize bool `url:"hotresize,omitempty" json:"hotresize,omitempty"` HotResize bool `url:"hotresize,omitempty" json:"hotresize,omitempty"`
@@ -46,10 +53,6 @@ type CreateRequest struct {
// Required: false // Required: false
Password string `url:"password,omitempty" json:"password,omitempty"` Password string `url:"password,omitempty" json:"password,omitempty"`
// Account ID to make the image exclusive
// Required: false
AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"`
// Username for upload binary media // Username for upload binary media
// Required: false // Required: false
UsernameDL string `url:"usernameDL,omitempty" json:"usernameDL,omitempty"` UsernameDL string `url:"usernameDL,omitempty" json:"usernameDL,omitempty"`
@@ -69,7 +72,6 @@ type CreateRequest struct {
// Binary architecture of this image // Binary architecture of this image
// Should be: // Should be:
// - X86_64 // - X86_64
// - PPC64_LE
// Required: false // Required: false
Architecture string `url:"architecture,omitempty" json:"architecture,omitempty" validate:"omitempty,imageArchitecture"` Architecture string `url:"architecture,omitempty" json:"architecture,omitempty" validate:"omitempty,imageArchitecture"`

View File

@@ -13,10 +13,6 @@ type DeleteRequest struct {
// ID of the image to delete // ID of the image to delete
// Required: true // Required: true
ImageID uint64 `url:"imageId" json:"imageId" validate:"required"` ImageID uint64 `url:"imageId" json:"imageId" validate:"required"`
// Whether to completely delete the image
// Required: false
Permanently bool `url:"permanently,omitempty" json:"permanently,omitempty"`
} }
// Delete deletes image by ID // Delete deletes image by ID

View File

@@ -16,7 +16,7 @@ type GetRequest struct {
// If set to False returns only images in status CREATED // If set to False returns only images in status CREATED
// Required: false // Required: false
ShowAll bool `url:"show_all,omitempty" json:"show_all,omitempty"` ShowAll bool `url:"showAll,omitempty" json:"showAll,omitempty"`
} }
// Get gets image by ID. // Get gets image by ID.

View File

@@ -4,6 +4,8 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"net/http" "net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// ListRequest struct to get list of available images // ListRequest struct to get list of available images
@@ -46,15 +48,19 @@ type ListRequest struct {
// Find by public True or False // Find by public True or False
// Required: false // Required: false
Public bool `url:"public,omitempty" json:"public,omitempty"` Public interface{} `url:"public,omitempty" json:"public,omitempty" validate:"omitempty,isBool"`
// Find by hot resize True or False // Find by hot resize True or False
// Required: false // Required: false
HotResize bool `url:"hotResize,omitempty" json:"hotResize,omitempty"` HotResize interface{} `url:"hotResize,omitempty" json:"hotResize,omitempty" validate:"omitempty,isBool"`
// Find by bootable True or False // Find by bootable True or False
// Required: false // Required: false
Bootable bool `url:"bootable,omitempty" json:"bootable,omitempty"` Bootable interface{} `url:"bootable,omitempty" json:"bootable,omitempty" validate:"omitempty,isBool"`
// Sort by one of supported fields, format +|-(field)
// Required: false
SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"`
// Page number // Page number
// Required: false // Required: false
@@ -63,10 +69,15 @@ type ListRequest struct {
// Page size // Page size
// Required: false // Required: false
Size uint64 `url:"size,omitempty" json:"size,omitempty"` Size uint64 `url:"size,omitempty" json:"size,omitempty"`
// Find by enabled True or False
// Required: false
Enabled interface{} `url:"enabled,omitempty" json:"enabled,omitempty" validate:"omitempty,isBool"`
} }
// List gets list of available images as a ListImages struct, optionally filtering by account ID // List gets list of available images as a ListImages struct, optionally filtering by account ID
func (i Image) List(ctx context.Context, req ListRequest) (*ListImages, error) { func (i Image) List(ctx context.Context, req ListRequest) (*ListImages, error) {
res, err := i.ListRaw(ctx, req) res, err := i.ListRaw(ctx, req)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -84,6 +95,11 @@ func (i Image) List(ctx context.Context, req ListRequest) (*ListImages, error) {
// ListRaw gets list of available images as an array of bytes // ListRaw gets list of available images as an array of bytes
func (i Image) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { func (i Image) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) {
if err := validators.ValidateRequest(req); err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/image/list" url := "/cloudapi/image/list"
res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req) res, err := i.client.DecortApiCall(ctx, http.MethodPost, url, req)

View File

@@ -35,6 +35,9 @@ type ItemImage struct {
// Name // Name
Name string `json:"name"` Name string `json:"name"`
// NetworkInterfaceNaming
NetworkInterfaceNaming string `json:"networkInterfaceNaming"`
// Pool // Pool
Pool string `json:"pool"` Pool string `json:"pool"`
@@ -104,6 +107,9 @@ type RecordImage struct {
// Bootable // Bootable
Bootable bool `json:"bootable"` Bootable bool `json:"bootable"`
// CdPresentedTo
CdPresentedTo interface{} `json:"cdPresentedTo"`
// ComputeCI ID // ComputeCI ID
ComputeCIID uint64 `json:"computeciId"` ComputeCIID uint64 `json:"computeciId"`
@@ -146,6 +152,9 @@ type RecordImage struct {
// Name // Name
Name string `json:"name"` Name string `json:"name"`
// NetworkInterfaceNaming
NetworkInterfaceNaming string `json:"networkInterfaceNaming"`
// Password // Password
Password string `json:"password"` Password string `json:"password"`

View File

@@ -4,13 +4,15 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"net/http" "net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// ListRequest struct to get list of information about images // ListRequest struct to get list of information about images
type ListRequest struct { type ListRequest struct {
// Find by ID // Find by ID
// Required: false // Required: false
ByID uint64 `url:"id,omitempty" json:"id,omitempty"` ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"`
// Find by name // Find by name
// Required: false // Required: false
@@ -36,6 +38,10 @@ type ListRequest struct {
// Required: false // Required: false
IncludeDisabled bool `url:"includeDisabled,omitempty" json:"includeDisabled,omitempty"` IncludeDisabled bool `url:"includeDisabled,omitempty" json:"includeDisabled,omitempty"`
// Sort by one of supported fields, format +|-(field)
// Required: false
SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"`
// Page number // Page number
// Required: false // Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"` Page uint64 `url:"page,omitempty" json:"page,omitempty"`
@@ -47,6 +53,7 @@ type ListRequest struct {
// List gets list of all k8ci catalog items available to the current user as a ListK8CI struct // List gets list of all k8ci catalog items available to the current user as a ListK8CI struct
func (k K8CI) List(ctx context.Context, req ListRequest) (*ListK8CI, error) { func (k K8CI) List(ctx context.Context, req ListRequest) (*ListK8CI, error) {
res, err := k.ListRaw(ctx, req) res, err := k.ListRaw(ctx, req)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -64,6 +71,11 @@ func (k K8CI) List(ctx context.Context, req ListRequest) (*ListK8CI, error) {
// ListRaw gets list of all k8ci catalog items available to the current user as an array of bytes // ListRaw gets list of all k8ci catalog items available to the current user as an array of bytes
func (k K8CI) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) { func (k K8CI) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) {
if err := validators.ValidateRequest(req); err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/k8ci/list" url := "/cloudapi/k8ci/list"
res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req)

View File

@@ -4,9 +4,11 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"net/http" "net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// ListDeletedRequest struct to get list information about deleted images // ListDeletedRequest struct to get list information about deleted k8ci items
type ListDeletedRequest struct { type ListDeletedRequest struct {
// Find by ID // Find by ID
// Required: false // Required: false
@@ -28,6 +30,10 @@ type ListDeletedRequest struct {
// Required: false // Required: false
NetworkPlugins string `url:"netPlugins,omitempty" json:"netPlugins,omitempty"` NetworkPlugins string `url:"netPlugins,omitempty" json:"netPlugins,omitempty"`
// Sort by one of supported fields, format +|-(field)
// Required: false
SortBy string `url:"sortBy,omitempty" json:"sortBy,omitempty" validate:"omitempty,sortBy"`
// Page number // Page number
// Required: false // Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"` Page uint64 `url:"page,omitempty" json:"page,omitempty"`
@@ -39,6 +45,11 @@ type ListDeletedRequest struct {
// ListDeleted gets list all deleted k8ci catalog items available to the current user // ListDeleted gets list all deleted k8ci catalog items available to the current user
func (k K8CI) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListK8CI, error) { func (k K8CI) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListK8CI, error) {
if err := validators.ValidateRequest(req); err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/k8ci/listDeleted" url := "/cloudapi/k8ci/listDeleted"
res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req) res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req)

View File

@@ -8,8 +8,6 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// type Params []string
// CreateRequest struct to create kubernetes cluster // CreateRequest struct to create kubernetes cluster
type CreateRequest struct { type CreateRequest struct {
// Name of Kubernetes cluster // Name of Kubernetes cluster
@@ -29,7 +27,7 @@ type CreateRequest struct {
WorkerGroupName string `url:"workerGroupName" json:"workerGroupName" validate:"required,workerGroupName"` WorkerGroupName string `url:"workerGroupName" json:"workerGroupName" validate:"required,workerGroupName"`
// Network plugin // Network plugin
// Must be one of these values: flannel, weawenet, calico // Must be one of these values: flannel, weavenet, calico
// Required: true // Required: true
NetworkPlugin string `url:"networkPlugin" json:"networkPlugin" validate:"required,networkPlugin"` NetworkPlugin string `url:"networkPlugin" json:"networkPlugin" validate:"required,networkPlugin"`
@@ -74,7 +72,7 @@ type CreateRequest struct {
// Master node RAM volume in MB // Master node RAM volume in MB
// Required: false // Required: false
MasterRAM uint `url:"masterRam,omitempty" json:"masterRam,omitempty"` MasterRAM uint64 `url:"masterRam,omitempty" json:"masterRam,omitempty"`
// Master node boot disk size in GB If 0 is specified, size is defined by the OS image size // Master node boot disk size in GB If 0 is specified, size is defined by the OS image size
// Required: false // Required: false
@@ -90,7 +88,7 @@ type CreateRequest struct {
// Worker node RAM volume in MB // Worker node RAM volume in MB
// Required: false // Required: false
WorkerRAM uint `url:"workerRam,omitempty" json:"workerRam,omitempty"` WorkerRAM uint64 `url:"workerRam,omitempty" json:"workerRam,omitempty"`
// Worker node boot disk size in GB. If 0 is specified, size is defined by the OS image size // Worker node boot disk size in GB. If 0 is specified, size is defined by the OS image size
// Required: false // Required: false
@@ -111,7 +109,7 @@ type CreateRequest struct {
// Custom sysctl values for Load Balancer instance. Applied on boot // Custom sysctl values for Load Balancer instance. Applied on boot
// Required: false // Required: false
LbSysctlParams string `url:"lbSysctlParams,omitempty" json:"lbSysctlParams,omitempty"` LbSysctlParams []map[string]interface{} `url:"lbSysctlParams,omitempty" json:"lbSysctlParams,omitempty"`
// Use Highly Available schema for LB deploy // Use Highly Available schema for LB deploy
// Required: false // Required: false
@@ -164,12 +162,22 @@ type CreateRequest struct {
// Insert ssl certificate in x509 pem format // Insert ssl certificate in x509 pem format
// Required: false // Required: false
OidcCertificate string `url:"oidcCertificate,omitempty" json:"oidcCertificate,omitempty"` OidcCertificate string `url:"oidcCertificate,omitempty" json:"oidcCertificate,omitempty"`
// Type of the emulated system, Q35 or i440fx
// Required: false
Chipset string `url:"chipset,omitempty" json:"chipset,omitempty"`
} }
// type wrapperCreateRequest struct { // GetRAM returns RAM field values
// CreateRequest func (r CreateRequest) GetRAM() map[string]uint64 {
// Params []string `url:"lbSysctlParams,omitempty"`
// } res := make(map[string]uint64, 2)
res["MasterRAM"] = r.MasterRAM
res["WorkerRAM"] = r.WorkerRAM
return res
}
// Create creates a new Kubernetes cluster in the specified Resource Group // Create creates a new Kubernetes cluster in the specified Resource Group
func (k8s K8S) Create(ctx context.Context, req CreateRequest) (string, error) { func (k8s K8S) Create(ctx context.Context, req CreateRequest) (string, error) {
@@ -178,31 +186,9 @@ func (k8s K8S) Create(ctx context.Context, req CreateRequest) (string, error) {
return "", validators.ValidationErrors(validators.GetErrors(err)) return "", validators.ValidationErrors(validators.GetErrors(err))
} }
// var params []string
// if len(req.LbSysctlParams) != 0 {
// params = make([]string, 0, len(req.LbSysctlParams))
// for r := range req.LbSysctlParams {
// b, err := json.Marshal(req.LbSysctlParams[r])
// if err != nil {
// return "", err
// }
// params = append(params, string(b))
// }
// } else {
// params = []string{"[]"}
// }
// reqWrapped := wrapperCreateRequest{
// CreateRequest: req,
// Params: params,
// }
url := "/cloudapi/k8s/create" url := "/cloudapi/k8s/create"
res, err := k8s.client.DecortApiCall(ctx, http.MethodPost, url, req) res, err := k8s.client.DecortApiCallMP(ctx, http.MethodPost, url, req)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@@ -17,7 +17,7 @@ type DeleteRequest struct {
// True if cluster is destroyed permanently. // True if cluster is destroyed permanently.
// Otherwise it can be restored from Recycle Bin // Otherwise it can be restored from Recycle Bin
// Required: true // Required: true
Permanently bool `url:"permanently" json:"permanently" validate:"required"` Permanently bool `url:"permanently" json:"permanently"`
} }
// Delete deletes kubernetes cluster // Delete deletes kubernetes cluster

View File

@@ -20,7 +20,7 @@ type DeleteMasterFromGroupRequest struct {
// List of Compute IDs of master nodes to delete // List of Compute IDs of master nodes to delete
// Required: true // Required: true
MasterIDs []string `url:"masterIds" json:"masterIds" validate:"min=1"` MasterIDs []uint64 `url:"masterIds" json:"masterIds" validate:"min=1"`
} }
// DeleteMasterFromGroup deletes compute from masters group in selected Kubernetes cluster // DeleteMasterFromGroup deletes compute from masters group in selected Kubernetes cluster

Some files were not shown because too many files have changed in this diff Show More