Compare commits

...

81 Commits
v1.4.0 ... main

Author SHA1 Message Date
dayterr
f111bf25fc v1.13.2 2025-12-23 17:39:58 +03:00
dayterr
055b256d6b v1.13.1 2025-12-16 14:22:57 +03:00
asteam
f0dee6360a v1.13.0 2025-12-08 16:16:35 +03:00
a267d35ddf v1.12.10 2025-11-18 14:42:39 +03:00
asteam
0bf073da93 v1.12.9 2025-11-14 17:38:59 +03:00
asteam
562b6019d0 v1.12.8 2025-10-14 16:44:25 +03:00
bf5c01b40b v1.12.7 2025-10-09 17:41:15 +03:00
dayterr
095a18f27a v1.12.6 2025-10-02 12:36:47 +03:00
asteam
84a090f9e8 v1.12.5 2025-09-26 12:21:29 +03:00
befff7acd9 V1.12.4 2025-09-19 13:45:58 +03:00
dayterr
abd35f858c v1.12.3 2025-09-11 15:56:44 +03:00
asteam
825b1a0a00 v1.12.2 2025-08-29 12:51:25 +03:00
asteam
e10ee7f801 v1.12.1 2025-08-01 15:36:29 +03:00
asteam
7dacf35cd6 v1.12.0 2025-07-15 17:39:18 +03:00
asteam
1f8637400f v1.11.6 2025-07-15 17:26:44 +03:00
89831894df v1.11.5 2025-06-26 13:21:52 +03:00
92431c5c65 v1.11.4 2025-05-21 13:39:23 +03:00
asteam
c7a2c4ed5a v1.11.3 2025-05-07 13:15:39 +03:00
0c44daa241 v1.11.2 2025-04-18 11:42:00 +03:00
asteam
650b1c158b v1.11.1 2025-04-11 11:17:52 +03:00
8a101c6fcb v1.11.0 2025-04-09 11:21:07 +03:00
asteam
3f21a89e80 v1.10.2 2025-03-11 13:09:17 +03:00
cbce7f434f v1.10.1 2025-02-07 11:11:43 +03:00
e04dc42d2b v1.10.0 2024-12-27 11:35:22 +03:00
asteam
88eb9e8898 v1.9.2 2024-12-04 11:50:22 +03:00
9ec34c6bfc v1.9.1 2024-11-22 12:09:50 +03:00
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
84c0248019 v1.6.8 2023-11-03 11:17:45 +03:00
4120cd2b1a v1.6.7 2023-10-25 17:37:18 +03:00
b666789c7d v1.6.6 2023-10-23 12:40:54 +03:00
b069c31745 v1.6.5 2023-10-11 12:18:27 +03:00
2953ef0a85 v1.6.4 2023-10-03 16:44:32 +03:00
50a4d5ade2 v1.6.3 2023-09-29 12:28:16 +03:00
1575b75fa6 v1.6.2 2023-09-28 19:34:23 +03:00
35fa4af0d6 v1.6.0 2023-09-28 15:37:28 +03:00
d3e6309ef8 v1.6.0-teta 2023-09-27 15:06:18 +03:00
78a4152471 v1.6.0-zeta 2023-09-25 19:11:33 +03:00
3e55195831 1.6.0-epsilon 2023-09-24 14:41:21 +03:00
9f5e76aee4 1.6.0-delta 2023-09-24 12:11:31 +03:00
df8b045e77 v1.5.8 2023-09-18 14:13:55 +03:00
4d9b8fc9d8 v1.5.7 2023-09-04 18:48:22 +03:00
e8270453cc 1.5.6 2023-08-30 13:54:30 +03:00
7de095302b v1.5.5 2023-08-25 13:53:43 +03:00
a3711057ba v1.5.4 2023-08-22 15:28:39 +03:00
1c59ca338a v1.5.3 2023-08-15 11:42:30 +03:00
f1529c9aac v1.5.2 2023-08-09 19:33:50 +03:00
040235f92f v1.5.1 2023-07-24 18:13:52 +03:00
4a152cb44c v1.5.0 2023-07-24 16:43:10 +03:00
c78b1348e3 v1.5.0 2023-07-24 15:13:04 +03:00
8f152a2f63 v1.5.0 2023-07-21 15:14:10 +03:00
0be4d8fb0c v1.5.0-epsilon 2023-07-13 18:32:21 +03:00
Никита Сорокин
5025a17ea4 v1.5.0-delta 2023-07-13 15:28:07 +03:00
7c787f6fce v1.5.0-gamma2 2023-07-07 12:40:03 +03:00
20fd7ab50c v1.5.0-gamma-qf 2023-06-30 11:24:26 +03:00
f50f71ab0d v1.5.0-gamma 2023-06-30 11:21:47 +03:00
29c7f143fe v1.4.7 2023-06-29 15:52:49 +03:00
264538f492 v1.4.6 2023-06-23 15:13:22 +03:00
c06a3198f6 v1.4.5 2023-06-19 15:06:31 +03:00
c9e4ae6afe v1.4.4 2023-06-01 16:50:10 +03:00
2a1593f45f v1.5.0-beta 2023-05-18 13:55:28 +03:00
190f24dac1 v1.4.3 2023-05-18 13:37:48 +03:00
256dba5134 v1.5.0-alfa 2023-05-15 19:27:02 +03:00
b7137683ab v1.4.2 2023-05-15 10:55:36 +03:00
10e3e19892 v1.4.1 2023-05-04 16:15:35 +03:00
1434 changed files with 59034 additions and 9846 deletions

10
.gitignore vendored
View File

@@ -1,2 +1,10 @@
cmd/ cmd/
.idea/ .idea/
.vscode/
.fleet/
.DS_Store
tests/platform_upgrade/.env
tests/platform_upgrade/input.json
tests/platform_upgrade/*.txt
tests/platform_upgrade/*.log
*.env

View File

@@ -1,44 +1,57 @@
## Version 1.4.0 ## Version 1.13.2
### Features Методы `Audits` в cloudapi/compute, cloudbroker/compute, cloudapi/account, cloudbroker/account, cloudapi/vins, cloudbroker/vins, cloudapi/rg и cloudbroker/rg стали deprecated и в следующих версиях будут удалены, вместо них необходимо использовать метод `List` в cloudapi/audit и cloudbroker/audit с соответствующими фильтрами
Метод `ListNodes` в cloudbroker/image стал deprecated и в следующих версиях будет удалён
- Actualized SDK to platform version 3.8.6: Методы `AccessGrant`, `AccessGrantToPool`, `AccessRevoke`, `AccessRevokeToPool` в cloudbroker/sep стали deprecated и в следующих версиях будут удалены
- Added required field networkPlugin to requests in: Методы `ComputeCISet`, `ComputeCIUnset`, `GetAudits` в cloudbroker/computeGetAudits и `GetAudits` в cloudbroker/compute стали deprecated и будут удалены в следующих версиях
- /cloudapi/cloudbroker/k8s/create;
- /cloudbroker/k8ci/create. Все методы группы `.SDN()` находятся в альфа-версии.
- Added networkPlugin field in models:
- /cloudapi/cloudbroker/k8s; ### Добавлено
- /cloudbroker/k8ci.
- Updated list of compute objects fields and added list of group objects in bservice model. #### audit
- Added cpuAllocationRatio and cpuAllocationParameter fields in models: | Идентификатор<br>задачи | Описание |
- /cloudapi/cloudbroker/rg; | --- | ---|
- /cloudapi/cloudbroker/account. | BGOS-706 | Поля `ResgroupID`, `AccountID`, `ComputeID` в структуру ответа `RecordAudit` в cloudbroker/audit |
- Added setCpuAllocationRatio endpoint support in:
- /cloudbroker/account; #### compute
- /cloudbroker/rg. | Идентификатор<br>задачи | Описание |
- Added /cloudbrocker/grid/setCpuAllocationRatioForVM endpoint support. | --- | --- |
- Added setCpuAllocationParameter endpoint support in: | BGOS-703 | Опциональное поле `NetMask` в структуру запроса `NetAttachRequest` в cloudbroker/compute |
- /cloudbroker/account; | BGOS-704 | Поле `SepID` в структуру ответа `InfoDisk` в cloudapi/compute и cloudbroker/compute |
- /cloudbroker/rg;
- /cloudbroker/grid. #### kvmx86
- Added cloudapi/cloudbroker/compute/changeLinkState endpoint support. | Идентификатор<br>задачи | Описание |
| --- | --- |
- Added enabled field in cloudapi/compute models: | BGOS-703 | Опциональное поле `NetMask` в структуры запросов `Interface` в cloudapi/kvmx86 и cloudbroker/kvmx86 |
- interfaces in compute/list response;
- RecordNetAttach (compute/netAttach response). #### sep
| Идентификатор<br>задачи | Описание |
- Added cloudapi/compute/bootOrderSet endpoint support. | --- | ---|
| BGOS-704 | Опциональное поле `SepIDs` в структуре запроса `ListRequest` в cloudbroker/sep |
- Added cloudapi/compute/bootOrderGet endpoint support.
#### storage policy
### Bug Fixes | Идентификатор<br>задачи | Описание |
| --- | ---|
- Fixed pciSlot field type in models: | BGOS-708 | Поле `SepTechStatus` в структуру запроса `ListRequest` в cloudapi/stpolicy и в cloudbroker/stpolicy |
- cloudapi/cloudbroker/computes; | BGOS-710 | Обязательное поле `AccessSEPsPool` в структуру запроса `UpdateRequest` в cloudbroker/stpolicy |
- cloudapi/cloudbroker/vins.
#### trunk
- Fixed handling cloudapi/account/restore endpoint response (panicked when marhalling). | Идентификатор<br>задачи | Описание |
| --- | ---|
- Added missing field diskType in cloudapi/compute/diskAttach request. | BGOS-705 | Поле `MTU` в структуру ответа `ItemTrunk` в cloudapi/trunk и в cloudbroker/trunk |
| BGOS-705 | Опциональное поле `MTU` в структуры запроса`CreateRequest` и `UpdateRequest` в cloudbroker/trunk |
- Added missing eBurst field in cloudapi/extnet QOS model.
### Изменено
#### compute
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BGOS-707 | Поле `Value` стало обязательным в структурах запроса `AntiAffinityRuleAddRequest`, `AntiAffinityRuleRemoveRequest`, `AffinityRuleAddRequest` и `AffinityRuleRemoveRequest` в cloudapi/compute и в cloudbroker/compute |
| BGOS-707 | Тип поля `StoragePolicyID` изменен с обязательного на опциональный в структуре запроса`CloneRequest` в cloudbroker/compute |
| BGOS-707 | Тип поля `ComputeID` с `uint64` на `string` в структуре запроса `GetCloneStatusRequest` в cloudbroker/compute |
#### node
| Идентификатор<br>задачи | Описание |
| --- | --- |
| BGOS-709 | Поле `VFNum` заменено на поле `FNID` в структуре `VFParam` в структуре запроса `SetVFsParamsRequest` в cloudbroker/node |

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.

1159
README.md

File diff suppressed because it is too large Load Diff

View File

@@ -1,62 +0,0 @@
# Decort SDK
Decort SDK is a library, written in GO (Golang) for interact with the **DECORT** API.
The library contents structures and methods for requesting to an user (cloudapi) and admin (cloudbroker) groups of API.
Also the library have structures for responses.
## Contents
- [Install](#install)
- [API List](#api-list)
- [Examples](#examples)
- [Examples2](#examples2)
## Install
```bash
go get -u repository.basistech.ru/BASIS/decort-golang-sdk
```
## API List
## Examples
```go
package main
import (
"context"
"fmt"
"log"
"repository.basistech.ru/BASIS/decort-golang-sdk/config"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/kvmx86"
)
func main() {
cfg := config.Config{
AppID: "<APPID>",
AppSecret: "<APPSECRET>",
SSOURL: "https://sso.digitalenergy.online",
DecortURL: "https://mr4.digitalenergy.online",
Retries: 5,
}
client := decort.New(cfg)
req := kvmx86.CreateRequest{
RGID: 123,
Name: "compute",
CPU: 4,
RAM: 4096,
ImageID: 321,
}
res, err := client.KVMX86().Create(context.Background(), req)
if err != nil {
log.Fatal(err)
}
fmt.Println(res)
}
```
## Examples2

106
check.go Normal file
View File

@@ -0,0 +1,106 @@
package decortsdk
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"strings"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/constants"
)
type CheckInfo struct {
Version string `json:"version"`
Build uint64 `json:"build"`
}
const versionURL = "/system/info/version"
func (de DecortClient) Check() (*CheckInfo, error) {
res, err := de.DecortApiCall(context.Background(), http.MethodGet, versionURL, nil)
if err != nil {
return nil, err
}
info := CheckInfo{}
err = json.Unmarshal([]byte(strings.Replace(strings.Trim(string(res), `"`), "\\", "", -1)), &info)
if err != nil {
var v string
json.Unmarshal([]byte(res), &v)
if _, exists := constants.VersionMap[v]; exists {
info.Version = v
} else {
return nil, fmt.Errorf("platform version isn't supported")
}
}
if v, ok := constants.VersionMap[info.Version]; ok {
if v == "-" {
return &info, nil
}
return nil, errors.New(fmt.Sprintf("SDK don't support platform version %s, please use %s SDK version", info.Version, v))
}
return nil, errors.New(fmt.Sprintf("platform version %s isn't supported", info.Version))
}
func (bvs BVSDecortClient) Check() (*CheckInfo, error) {
res, err := bvs.DecortApiCall(context.Background(), http.MethodGet, versionURL, nil)
if err != nil {
return nil, err
}
info := CheckInfo{}
err = json.Unmarshal([]byte(strings.Replace(strings.Trim(string(res), `"`), "\\", "", -1)), &info)
if err != nil {
var v string
json.Unmarshal([]byte(res), &v)
if _, exists := constants.VersionMap[v]; exists {
info.Version = v
} else {
return nil, fmt.Errorf("platform version isn't supported")
}
}
if v, ok := constants.VersionMap[info.Version]; ok {
if v == "-" {
return &info, nil
}
return nil, errors.New(fmt.Sprintf("SDK don't support platform version %s, please use %s SDK version", info.Version, v))
}
return nil, errors.New(fmt.Sprintf("platform version %s isn't supported", info.Version))
}
func (ldc LegacyDecortClient) Check() (*CheckInfo, error) {
res, err := ldc.DecortApiCall(context.Background(), http.MethodGet, versionURL, nil)
if err != nil {
return nil, err
}
info := CheckInfo{}
err = json.Unmarshal([]byte(strings.Replace(strings.Trim(string(res), `"`), "\\", "", -1)), &info)
if err != nil {
var v string
json.Unmarshal([]byte(res), &v)
if _, exists := constants.VersionMap[v]; exists {
info.Version = v
} else {
return nil, fmt.Errorf("platform version isn't supported")
}
}
if v, ok := constants.VersionMap[info.Version]; ok {
if v == "-" {
return &info, nil
}
return nil, errors.New(fmt.Sprintf("SDK don't support platform version %s, please use %s SDK version", info.Version, v))
}
return nil, errors.New(fmt.Sprintf("platform version %s isn't supported", info.Version))
}

410
client.go
View File

@@ -1,24 +1,37 @@
package decortsdk package decortsdk
import ( import (
"bytes"
"context" "context"
"errors" "crypto/tls"
"encoding/base64"
"encoding/json"
"fmt"
"io" "io"
"mime/multipart"
"net/http" "net/http"
"reflect"
"strconv"
"strings" "strings"
"sync"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi" "time"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker"
"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/client" "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/cloudbroker"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/sdn"
) )
// 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
cfg config.Config
expiryTime time.Time
mutex *sync.Mutex
} }
// Сlient builder // Сlient builder
@@ -29,7 +42,16 @@ func New(cfg config.Config) *DecortClient {
return &DecortClient{ return &DecortClient{
decortURL: cfg.DecortURL, decortURL: cfg.DecortURL,
client: client.NewHttpClient(cfg), client: &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
//nolint:gosec
InsecureSkipVerify: cfg.SSLSkipVerify,
},
},
},
cfg: trimConfig(&cfg),
mutex: &sync.Mutex{},
} }
} }
@@ -43,33 +65,385 @@ func (dc *DecortClient) CloudBroker() *cloudbroker.CloudBroker {
return cloudbroker.New(dc) return cloudbroker.New(dc)
} }
// SDN builder
func (dc *DecortClient) SDN() *sdn.SDN {
return sdn.New(dc)
}
// 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) {
values, err := query.Values(params)
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, dc.decortURL+constants.RESTMACHINE+url, body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
body := strings.NewReader(values.Encode()) // get token
req, err := http.NewRequestWithContext(ctx, method, dc.decortURL+"/restmachine"+url, body) if err = dc.getToken(ctx); err != nil {
return nil, err
}
// perform request
respBytes, err := dc.do(req, ctype)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return respBytes, err
}
// DecortApiCallCtype method for sending requests to the platform with content type
func (dc *DecortClient) DecortApiCallCtype(ctx context.Context, method, url, ctype string, params interface{}) ([]byte, error) {
var body *bytes.Buffer
switch ctype {
case constants.MIMESTREAM:
body = bytes.NewBuffer(params.([]byte))
case constants.MIMEJSON:
jsonBody, err := json.Marshal(params)
if err != nil {
return nil, err
}
body = bytes.NewBuffer(jsonBody)
default:
ctype = constants.MIMEPOSTForm
values, err := query.Values(params)
if err != nil {
return nil, err
}
body = bytes.NewBufferString(values.Encode())
}
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 {
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 {
return nil, err
}
// perform request
respBytes, err := dc.do(req, ctype)
if err != nil {
return nil, err
}
return respBytes, err
}
func (dc *DecortClient) getToken(ctx context.Context) error {
dc.mutex.Lock()
defer dc.mutex.Unlock()
// new token is not needed
if dc.cfg.Token != "" && !time.Now().After(dc.expiryTime) {
return nil
}
// 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) resp, err := dc.client.Do(req)
if err != nil { if err != nil || resp == nil {
return nil, err return fmt.Errorf("cannot get token: %w", err)
} }
defer resp.Body.Close() defer resp.Body.Close()
respBytes, err := io.ReadAll(resp.Body) 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)
expiryTime, err := getTokenExp(token)
if err != nil {
return fmt.Errorf("cannot get expiry time: %w", err)
}
dc.cfg.Token = token
dc.expiryTime = expiryTime
return nil
}
// 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 != "" {
req.Header.Set("Content-Type", ctype)
}
req.Header.Add("Authorization", "bearer "+dc.cfg.Token)
req.Header.Set("Accept", "application/json")
buf, err := io.ReadAll(req.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if resp.StatusCode != 200 { req.Body.Close()
return nil, errors.New(string(respBytes)) req.Body = io.NopCloser(bytes.NewBuffer(buf))
resp, err := dc.client.Do(req)
if resp != nil {
defer resp.Body.Close()
} }
return respBytes, nil // 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 || resp.StatusCode == 204 {
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)
}
// 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{}
writer := multipart.NewWriter(reqBody)
values := reflect.ValueOf(params)
types := values.Type()
defer writer.Close()
for i := 0; i < values.NumField(); i++ {
if !values.Field(i).IsValid() {
continue
}
if values.Field(i).IsZero() {
continue
}
if file, ok := constants.FileName[types.Field(i).Name]; ok {
part, err := writer.CreateFormFile(trimString(types.Field(i)), file)
if err != nil {
return &bytes.Buffer{}, "", err
}
_, err = io.Copy(part, strings.NewReader(valueToString(values.Field(i).Interface())))
if err != nil {
return &bytes.Buffer{}, "", err
}
continue
}
if values.Field(i).Type().Kind() == reflect.Slice {
switch slice := values.Field(i).Interface().(type) {
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
}
}
ct := writer.FormDataContentType()
return reqBody, ct, nil
}
func valueToString(a any) string {
switch str := a.(type) {
case string:
return str
case uint:
return strconv.FormatUint(uint64(str), 10)
case uint64:
return strconv.FormatUint(str, 10)
case bool:
return strconv.FormatBool(str)
default:
return ""
}
}
func trimString(el reflect.StructField) string {
return strings.TrimSuffix(el.Tag.Get("url"), ",omitempty")
}
func trimConfig(cfg *config.Config) config.Config {
cfg.SSOURL = strings.TrimSuffix(cfg.SSOURL, "/")
cfg.DecortURL = strings.TrimSuffix(cfg.DecortURL, "/")
return *cfg
}
func getTokenExp(token string) (time.Time, error) {
parts := strings.Split(token, ".")
if len(parts) != 3 {
return time.Time{}, fmt.Errorf("invalid token format")
}
payload, err := base64.RawURLEncoding.DecodeString(parts[1])
if err != nil {
return time.Time{}, fmt.Errorf("error decode payload from token: %w", err)
}
var claims map[string]interface{}
if err := json.Unmarshal(payload, &claims); err != nil {
return time.Time{}, err
}
exp, ok := claims["exp"]
if !ok {
return time.Time{}, fmt.Errorf("exp time bot found")
}
expTime := time.Unix(int64(exp.(float64)), 0)
return expTime, nil
} }

467
client_bvs.go Normal file
View File

@@ -0,0 +1,467 @@
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"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/sdn"
)
// 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)
}
// SDN builder
func (bdc *BVSDecortClient) SDN() *sdn.SDN {
return sdn.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
}
// DecortApiCallCtype method for sending requests to the platform with content type
func (bdc *BVSDecortClient) DecortApiCallCtype(ctx context.Context, method, url, ctype string, params interface{}) ([]byte, error) {
var body *bytes.Buffer
switch ctype {
case constants.MIMESTREAM:
body = bytes.NewBuffer(params.([]byte))
case constants.MIMEJSON:
jsonBody, err := json.Marshal(params)
if err != nil {
return nil, err
}
body = bytes.NewBuffer(jsonBody)
default:
ctype = constants.MIMEPOSTForm
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
}

32
client_mock.go Normal file
View File

@@ -0,0 +1,32 @@
package decortsdk
import (
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/sdn"
)
type MockDecortClient struct {
apiCaller *MockCaller
}
func NewMockDecortClient(apiCaller *MockCaller) ClientInterface {
return &MockDecortClient{
apiCaller: apiCaller,
}
}
// CloudAPI builder
func (mdc *MockDecortClient) CloudAPI() *cloudapi.CloudAPI {
return cloudapi.New(mdc.apiCaller)
}
// CloudBroker builder
func (mdc *MockDecortClient) CloudBroker() *cloudbroker.CloudBroker {
return cloudbroker.New(mdc.apiCaller)
}
// SDN builder
func (mdc *MockDecortClient) SDN() *sdn.SDN {
return sdn.New(mdc.apiCaller)
}

86
client_mock_gen.go Normal file
View File

@@ -0,0 +1,86 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: interfaces/caller.go
//
// Generated by this command:
//
// mockgen -package decortsdk -source interfaces/caller.go
//
// Package decortsdk is a generated GoMock package.
package decortsdk
import (
context "context"
reflect "reflect"
gomock "go.uber.org/mock/gomock"
)
// MockCaller is a mock of Caller interface.
type MockCaller struct {
ctrl *gomock.Controller
recorder *MockCallerMockRecorder
isgomock struct{}
}
// MockCallerMockRecorder is the mock recorder for MockCaller.
type MockCallerMockRecorder struct {
mock *MockCaller
}
// NewMockCaller creates a new mock instance.
func NewMockCaller(ctrl *gomock.Controller) *MockCaller {
mock := &MockCaller{ctrl: ctrl}
mock.recorder = &MockCallerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockCaller) EXPECT() *MockCallerMockRecorder {
return m.recorder
}
// DecortApiCall mocks base method.
func (m *MockCaller) DecortApiCall(ctx context.Context, method, url string, params any) ([]byte, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DecortApiCall", ctx, method, url, params)
ret0, _ := ret[0].([]byte)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// DecortApiCall indicates an expected call of DecortApiCall.
func (mr *MockCallerMockRecorder) DecortApiCall(ctx, method, url, params any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecortApiCall", reflect.TypeOf((*MockCaller)(nil).DecortApiCall), ctx, method, url, params)
}
// DecortApiCallCtype mocks base method.
func (m *MockCaller) DecortApiCallCtype(ctx context.Context, method, url, ctype string, params any) ([]byte, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DecortApiCallCtype", ctx, method, url, ctype, params)
ret0, _ := ret[0].([]byte)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// DecortApiCallCtype indicates an expected call of DecortApiCallCtype.
func (mr *MockCallerMockRecorder) DecortApiCallCtype(ctx, method, url, ctype, params any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecortApiCallCtype", reflect.TypeOf((*MockCaller)(nil).DecortApiCallCtype), ctx, method, url, ctype, params)
}
// DecortApiCallMP mocks base method.
func (m *MockCaller) DecortApiCallMP(ctx context.Context, method, url string, params any) ([]byte, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DecortApiCallMP", ctx, method, url, params)
ret0, _ := ret[0].([]byte)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// DecortApiCallMP indicates an expected call of DecortApiCallMP.
func (mr *MockCallerMockRecorder) DecortApiCallMP(ctx, method, url, params any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DecortApiCallMP", reflect.TypeOf((*MockCaller)(nil).DecortApiCallMP), ctx, method, url, params)
}

View File

@@ -3,6 +3,7 @@ package config
import ( import (
"encoding/json" "encoding/json"
"os" "os"
"time"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
@@ -41,9 +42,18 @@ type Config struct {
// Required: false // Required: false
Retries uint64 `json:"retries" yaml:"retries"` Retries uint64 `json:"retries" yaml:"retries"`
// Skip verify, true by default // Skip verify
// Required: false // Required: false
SSLSkipVerify bool `json:"sslSkipVerify" yaml:"sslSkipVerify"` SSLSkipVerify bool `json:"sslSkipVerify" yaml:"sslSkipVerify"`
// HTTP client timeout, unlimited if left empty
// Required: false
Timeout Duration `json:"timeout" yaml:"timeout"`
}
// SetTimeout is used to set HTTP client timeout.
func (c *Config) SetTimeout(dur time.Duration) {
c.Timeout = Duration(dur)
} }
// ParseConfigJSON parses Config from specified JSON-formatted file. // ParseConfigJSON parses Config from specified JSON-formatted file.
@@ -62,9 +72,7 @@ func ParseConfigJSON(path string) (Config, error) {
err = validators.ValidateConfig(config) err = validators.ValidateConfig(config)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return Config{}, validators.ValidationErrors(validators.GetErrors(err))
return Config{}, validators.ValidationError(validationError)
}
} }
return config, nil return config, nil
@@ -86,9 +94,7 @@ func ParseConfigYAML(path string) (Config, error) {
err = validators.ValidateConfig(config) err = validators.ValidateConfig(config)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return Config{}, validators.ValidationErrors(validators.GetErrors(err))
return Config{}, validators.ValidationError(validationError)
}
} }
return config, nil return config, nil

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

@@ -3,6 +3,7 @@ package config
import ( import (
"encoding/json" "encoding/json"
"os" "os"
"time"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
@@ -35,9 +36,18 @@ type LegacyConfig struct {
// Required: false // Required: false
Retries uint64 `json:"retries" yaml:"retries"` Retries uint64 `json:"retries" yaml:"retries"`
// Skip verify, true by default // Skip verify
// Required: false // Required: false
SSLSkipVerify bool `json:"sslSkipVerify" yaml:"sslSkipVerify"` SSLSkipVerify bool `json:"sslSkipVerify" yaml:"sslSkipVerify"`
// HTTP client timeout, unlimited if left empty
// Required: false
Timeout Duration `json:"timeout" yaml:"timeout"`
}
// SetTimeout is used to set HTTP client timeout.
func (c *LegacyConfig) SetTimeout(dur time.Duration) {
c.Timeout = Duration(dur)
} }
// ParseLegacyConfigJSON parses LegacyConfig from specified JSON-formatted file. // ParseLegacyConfigJSON parses LegacyConfig from specified JSON-formatted file.
@@ -56,9 +66,7 @@ func ParseLegacyConfigJSON(path string) (LegacyConfig, error) {
err = validators.ValidateConfig(config) err = validators.ValidateConfig(config)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return LegacyConfig{}, validators.ValidationErrors(validators.GetErrors(err))
return LegacyConfig{}, validators.ValidationError(validationError)
}
} }
return config, nil return config, nil
@@ -80,9 +88,7 @@ func ParseLegacyConfigYAML(path string) (LegacyConfig, error) {
err = validators.ValidateConfig(config) err = validators.ValidateConfig(config)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return LegacyConfig{}, validators.ValidationErrors(validators.GetErrors(err))
return LegacyConfig{}, validators.ValidationError(validationError)
}
} }
return config, nil return config, nil

54
config/timeouts.go Normal file
View File

@@ -0,0 +1,54 @@
package config
import (
"encoding/json"
"fmt"
"time"
)
// Duration is a wrapper around time.Duration (used for better user experience)
type Duration time.Duration
func (d *Duration) UnmarshalYAML(unmarshal func(interface{}) error) error {
var v interface{}
if err := unmarshal(&v); err != nil {
return err
}
switch value := v.(type) {
case string:
tmp, err := time.ParseDuration(value)
if err != nil {
return err
}
*d = Duration(tmp)
return nil
case float64:
return nil
default:
return fmt.Errorf("invalid duration %v", value)
}
}
func (d *Duration) UnmarshalJSON(b []byte) error {
var v interface{}
if err := json.Unmarshal(b, &v); err != nil {
return err
}
switch value := v.(type) {
case string:
tmp, err := time.ParseDuration(value)
if err != nil {
return err
}
*d = Duration(tmp)
return nil
case float64:
return nil
default:
return fmt.Errorf("invalid duration %v", value)
}
}
func (d *Duration) Get() time.Duration {
return time.Duration(*d)
}

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"`
}

21
go.mod
View File

@@ -1,18 +1,25 @@
module repository.basistech.ru/BASIS/decort-golang-sdk module repository.basistech.ru/BASIS/decort-golang-sdk
go 1.20 go 1.24.0
require ( require (
github.com/go-playground/validator/v10 v10.11.2 github.com/go-playground/validator/v10 v10.28.0
github.com/google/go-querystring v1.1.0 github.com/google/go-querystring v1.1.0
github.com/joho/godotenv v1.5.1
github.com/stretchr/testify v1.9.0
go.uber.org/mock v0.6.0
gopkg.in/yaml.v3 v3.0.1
) )
require ( require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.10 // indirect
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/leodido/go-urn v1.2.1 // indirect github.com/google/go-cmp v0.5.9 // indirect
golang.org/x/crypto v0.5.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect
golang.org/x/sys v0.4.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/text v0.6.0 // indirect golang.org/x/crypto v0.42.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect golang.org/x/sys v0.36.0 // indirect
golang.org/x/text v0.29.0 // indirect
) )

39
go.sum
View File

@@ -1,31 +1,38 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0=
github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
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.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688=
github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s= github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU=
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/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
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 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
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/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -6,4 +6,10 @@ 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)
// DecortApiCallCtype method for sending requests to the platform
DecortApiCallCtype(ctx context.Context, method, url, ctype 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

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,37 @@
package constants
const (
RESTMACHINE = "/restmachine"
)
const (
MIMEJSON = "application/json"
MIMEHTML = "text/html"
MIMEXML = "application/xml"
MIMEXML2 = "text/xml"
MIMEPlain = "text/plain"
MIMEPOSTForm = "application/x-www-form-urlencoded"
MIMEMultipartPOSTForm = "multipart/form-data"
MIMEPROTOBUF = "application/x-protobuf"
MIMEMSGPACK = "application/x-msgpack"
MIMEMSGPACK2 = "application/msgpack"
MIMEYAML = "application/x-yaml"
MIMEYAML2 = "application/yaml"
MIMETOML = "application/toml"
MIMESTREAM = "application/octet-stream"
)
var FileName = map[string]string{
"OidcCertificate": "ca.crt",
}
var K8sValues = []string{"labels", "taints", "annotations, additionalSANs"}
var VersionMap = map[string]string{
"4.5.0": "-",
"4.4.0": "-",
"4.3.0": "-",
"4.2.0": "-",
"4.1.1": "-",
"4.1.0": "-",
}

View File

@@ -0,0 +1,41 @@
package multierror
func Join(errs ...error) error {
n := 0
for _, err := range errs {
if err != nil {
n++
}
}
if n == 0 {
return nil
}
e := &joinError{
errs: make([]error, 0, n),
}
for _, err := range errs {
if err != nil {
e.errs = append(e.errs, err)
}
}
return e
}
type joinError struct {
errs []error
}
func (e *joinError) Error() string {
var b []byte
for i, err := range e.errs {
if i > 0 {
b = append(b, '\n')
}
b = append(b, err.Error()...)
}
return string(b)
}
func (e *joinError) Unwrap() []error {
return e.errs
}

View File

@@ -1,23 +1,38 @@
package validators package validators
import ( import (
"github.com/go-playground/validator/v10" "errors"
"fmt"
"net/url"
"reflect"
"regexp" "regexp"
"strconv"
"strings" "strings"
"github.com/go-playground/validator/v10"
"repository.basistech.ru/BASIS/decort-golang-sdk/interfaces"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/multierror"
) )
// 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)
}
// apiGroupValidator is used to validate APIGroup fields
func apiGroupValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
return IsInSlice(fieldValue, apiGroupValues)
} }
// 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.
@@ -28,7 +43,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
} }
} }
@@ -36,60 +51,81 @@ func resTypesValidator(fe validator.FieldLevel) bool {
return true return true
} }
// driverValidator is used to validate Driver fields.
func driverValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
return StringInSlice(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)
}
// securityGroupDirectionValidator is used to validate Direction field
func securityGroupDirectionValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
return IsInSlice(fieldValue, securityGroupDirectionValues)
}
// securityGroupEthertypeValidator is used to validate Ethertype field
func securityGroupEthertypeValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
return IsInSlice(fieldValue, securityGroupEthertypeValues)
}
// securityGroupProtocolValidator is used to validate Protocol field
func securityGroupProtocolValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
return IsInSlice(fieldValue, securityGroupProtocolValues)
} }
// computeOrderValidator is used to validate Order field. // computeOrderValidator is used to validate Order field.
@@ -100,7 +136,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
} }
} }
@@ -112,100 +148,84 @@ 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. // massCreateTypeValidator is used to validate net type field when mass creating kvm
func kvmNetTypeValidator(fe validator.FieldLevel) bool { func massCreateTypeValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String() fieldValue := fe.Field().String()
return StringInSlice(fieldValue, kvmNetTypeValues) return IsInSlice(fieldValue, massCreateNetTypeValues)
} }
// 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.
func imageDriversValidator(fe validator.FieldLevel) bool {
fieldSlice, ok := fe.Field().Interface().([]string)
if !ok {
return false
}
for _, item := range fieldSlice {
if !StringInSlice(item, imageDriversValues) {
return false
}
}
return true
} }
// imageArchitectureValidator is used to validate Architecture field. // imageArchitectureValidator is used to validate Architecture field.
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.
@@ -222,7 +242,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
@@ -235,7 +255,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
} }
} }
@@ -247,12 +267,214 @@ 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
func workerGroupNameValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
fieldValue = strings.Trim(fieldValue, " ")
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
}
func chipsetValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
return IsInSlice(fieldValue, chipsetValues)
}
func preferredCPUValidator(fe validator.FieldLevel) bool {
fieldSlice, ok := fe.Field().Interface().([]int64)
if !ok {
return false
}
for _, value := range fieldSlice {
if value < -1 {
return false
}
}
return true
}
// loaderTypeValidator is used to validate loaderType fields
func loaderTypeValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
return IsInSlice(fieldValue, loaderTypeValues)
}
// languageValidator is used to validate language fields
func languageValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
return IsInSlice(fieldValue, languageValues)
}
func userProviderValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
return IsInSlice(fieldValue, userProviders)
}
// sepTypeValidator is used to validate sepType fields
func sepTypeValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
return IsInSlice(fieldValue, sepTypeValues)
}
// deviceValidator is used to validate extnet device fields
func deviceValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
return IsInSlice(fieldValue, deviceValues)
}
// ipTypesValidator is used to validate ip types version fields
func ipTypesValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
return IsInSlice(fieldValue, ipTypeValues)
}
// 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...)
}
// trunkTagsValidator checks if trunk_tags is in range from 1 to 4095
func trunkTagsValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
numFieldValue, err := strconv.ParseInt(fieldValue, 10, 64)
if err != nil {
return false
}
return uint64(numFieldValue) >= uint64(trunkTagsMin) && uint64(numFieldValue) <= uint64(trunkTagsMax)
}
// addressPoolNetTypeValidator is used to validate NetAddressType fields
func addressPoolNetTypeValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
return IsInSlice(fieldValue, addressPoolNetTypeValues)
}
// sepTechStatusValidator is used to validate SepTechStatus fields
func sepTechStatusValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
return IsInSlice(fieldValue, sepTechStatusValues)
} }

View File

@@ -2,6 +2,7 @@ package validators
import ( import (
"errors" "errors"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/multierror"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
) )
@@ -20,12 +21,20 @@ func ValidationError(fe validator.FieldError) error {
return errors.New(errorMessage(fe)) return errors.New(errorMessage(fe))
} }
func ValidationErrors(fes []validator.FieldError) error {
errs := make([]error, 0, len(fes))
for _, fe := range fes {
errs = append(errs, ValidationError(fe))
}
return multierror.Join(errs...)
}
//nolint:errorlint //nolint:errorlint
func GetErrors(err error) validator.ValidationErrors { 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
@@ -33,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,12 +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":
case "driver": return fmt.Sprintf("%s %s: must be bool type", prefix, fe.Field())
return fmt.Sprintf("%s %s must be one of the following: %s",
prefix,
fe.Field(),
joinValues(driverValues))
case "accessType": case "accessType":
return fmt.Sprintf("%s %s must be one of the following: %s", return fmt.Sprintf("%s %s must be one of the following: %s",
@@ -50,6 +46,13 @@ func errorMessage(fe validator.FieldError) string {
fe.Field(), fe.Field(),
joinValues(protoValues)) joinValues(protoValues))
// apiGroup Validators
case "apiGroup":
return fmt.Sprintf("%s %s must be one of the following: %s",
prefix,
fe.Field(),
joinValues(apiGroupValues))
// Account Validators // Account Validators
case "accountCUType": case "accountCUType":
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 +92,18 @@ 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 "preferredCPU":
return fmt.Sprint(prefix, fe.Field(), "must be equal to or greater than", -1)
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,
@@ -121,12 +136,18 @@ func errorMessage(fe validator.FieldError) string {
fe.Field(), fe.Field(),
joinValues(flipgroupClientTypeValues)) joinValues(flipgroupClientTypeValues))
// KVM_X86/KVM_PPC Validators // k8s Validators
case "kvmNetType": case "workerGroupName":
return fmt.Sprintf("%s %s must be more 3 symbol",
prefix,
fe.Field())
// KVM_X86 Mass create validators
case "massCreateNetType":
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,
fe.Field(), fe.Field(),
joinValues(kvmNetTypeValues)) joinValues(massCreateNetTypeValues))
// LB Validators // LB Validators
case "lbAlgorithm": case "lbAlgorithm":
@@ -168,12 +189,6 @@ func errorMessage(fe validator.FieldError) string {
fe.Field(), fe.Field(),
joinValues(imageTypeValues)) joinValues(imageTypeValues))
case "imageDrivers":
return fmt.Sprintf("%s %s must contain only the following: %s",
prefix,
fe.Field(),
joinValues(imageDriversValues))
case "imageArchitecture": case "imageArchitecture":
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,
@@ -217,6 +232,146 @@ 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))
case "chipset":
return fmt.Sprintf("%s %s must be one of the following: %s",
prefix,
fe.Field(),
joinValues(chipsetValues))
case "loaderType":
return fmt.Sprintf("%s %s must be one of the following: %s",
prefix,
fe.Field(),
joinValues(loaderTypeValues))
case "language":
return fmt.Sprintf("%s %s must be one of the following: %s",
prefix,
fe.Field(),
joinValues(languageValues))
case "sepType":
return fmt.Sprintf("%s %s must be one of the following: %s",
prefix,
fe.Field(),
joinValues(sepTypeValues))
// user validators
case "userProvider":
return fmt.Sprintf("%s %s must be one of the following: %s",
prefix,
fe.Field(),
joinValues(userProviders))
// security group validators
case "securityGroupDirection":
return fmt.Sprintf("%s %s must be one of the following: %s",
prefix,
fe.Field(),
joinValues(securityGroupDirectionValues))
case "securityGroupEthertype":
return fmt.Sprintf("%s %s must be one of the following: %s",
prefix,
fe.Field(),
joinValues(securityGroupEthertypeValues))
case "securityGroupProtocol":
return fmt.Sprintf("%s %s must be one of the following: %s",
prefix,
fe.Field(),
joinValues(securityGroupProtocolValues))
// trunk tags validator
case "trunkTags":
return fmt.Sprintf("%s %s must be in range from 1 to 4095",
prefix,
fe.Field())
// addressPoolNetTypeValidator validator
case "addressPoolNetTypeValidator":
return fmt.Sprintf("%s %s must be one of the following: %s",
prefix,
fe.Field(),
joinValues(addressPoolNetTypeValues))
case "device":
return fmt.Sprintf("%s %s must be one of the following: %s",
prefix,
fe.Field(),
joinValues(deviceValues))
case "ipTypes":
return fmt.Sprintf("%s %s must be one of the following: %s",
prefix,
fe.Field(),
joinValues(ipTypeValues))
case "sepTechStatus":
return fmt.Sprintf("%s %s must be one of the following: %s",
prefix,
fe.Field(),
joinValues(sepTechStatusValues))
} }
return fe.Error() return fe.Error()

View File

@@ -7,38 +7,35 @@ import (
) )
var ( var (
once sync.Once once sync.Once
instance *DecortValidator decortValidator = validator.New()
) )
type DecortValidator struct {
decortValidator *validator.Validate
}
// getDecortValidator returns singleton instance of DecortValidator. // getDecortValidator returns singleton instance of DecortValidator.
func getDecortValidator() *validator.Validate { func getDecortValidator() *validator.Validate {
if instance == nil { once.Do(func() {
once.Do(func() { err := registerAllValidators(decortValidator)
instance = new(DecortValidator) if err != nil {
instance.decortValidator = validator.New() panic(err)
}
})
err := registerAllValidators(instance.decortValidator) return decortValidator
if err != nil {
panic(err)
}
})
}
return instance.decortValidator
} }
// 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
} }
err = validate.RegisterValidation("apiGroup", apiGroupValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("accessType", accessTypeValidator) err = validate.RegisterValidation("accessType", accessTypeValidator)
if err != nil { if err != nil {
return err return err
@@ -49,11 +46,6 @@ func registerAllValidators(validate *validator.Validate) error {
return err return err
} }
err = validate.RegisterValidation("driver", driverValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("imageBootType", imageBootTypeValidator) err = validate.RegisterValidation("imageBootType", imageBootTypeValidator)
if err != nil { if err != nil {
return err return err
@@ -64,11 +56,6 @@ func registerAllValidators(validate *validator.Validate) error {
return err return err
} }
err = validate.RegisterValidation("imageDrivers", imageDriversValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("imageArchitecture", imageArchitectureValidator) err = validate.RegisterValidation("imageArchitecture", imageArchitectureValidator)
if err != nil { if err != nil {
return err return err
@@ -109,6 +96,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
@@ -129,7 +121,7 @@ func registerAllValidators(validate *validator.Validate) error {
return err return err
} }
err = validate.RegisterValidation("kvmNetType", kvmNetTypeValidator) err = validate.RegisterValidation("massCreateNetType", massCreateTypeValidator)
if err != nil { if err != nil {
return err return err
} }
@@ -184,5 +176,145 @@ 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)
if err != nil {
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
}
err = validate.RegisterValidation("chipset", chipsetValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("preferredCPU", preferredCPUValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("loaderType", loaderTypeValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("language", languageValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("userProvider", userProviderValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("sepType", sepTypeValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("device", deviceValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("trunkTags", trunkTagsValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("securityGroupDirection", securityGroupDirectionValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("securityGroupEthertype", securityGroupEthertypeValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("securityGroupProtocol", securityGroupProtocolValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("addressPoolNetTypeValidator", addressPoolNetTypeValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("ipTypes", ipTypesValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("sepTechStatus", sepTechStatusValidator)
if err != nil {
return err
}
return nil return nil
} }

View File

@@ -1,46 +1,94 @@
package validators package validators
var ( var (
driverValues = []string{"KVM_X86", "KVM_PPC"} apiGroupValues = []string{"cloudapi", "cloudbroker", "system"}
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"}
accountCUTypeValues = []string{"CU_M", "CU_C", "CU_D", "CU_S", "CU_A", "CU_NO", "CU_I", "CU_NP"} accountCUTypeValues = []string{"CU_M", "CU_C", "CU_D", "CU_DM", "CU_I", "gpu_units"}
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", "SDN", "EMPTY", "TRUNK"}
computeDataDisksValues = []string{"KEEP", "DETACH", "DESTROY"} computeOrderValues = []string{"cdrom", "network", "hd"}
computeDataDisksValues = []string{"KEEP", "DETACH", "DESTROY"}
diskTypeValues = []string{"B", "T", "D"} diskTypeValues = []string{"B", "T", "D"}
flipgroupClientTypeValues = []string{"compute", "vins"} flipgroupClientTypeValues = []string{"compute", "vins"}
kvmNetTypeValues = []string{"EXTNET", "VINS", "NONE"} massCreateNetTypeValues = []string{"EXTNET", "VINS", "TRUNK"}
kvmx86NetTypeValues = []string{"EXTNET", "VINS", "EMPTY", "VFNIC", "DPDK", "SDN", "TRUNK"}
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", "unknown"}
imageDriversValues = []string{"KVM_X86"} imageArchitectureValues = []string{"X86_64"}
imageArchitectureValues = []string{"X86_64", "PPC64_LE"}
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{"is_powered", "power_on", "shutdown", "force_shutdown", "reboot"}
vmActionValues = []string{"stop", "move"}
computeFeaturesValues = []string{"hugepages", "numa", "cpupin", "vfnic", "dpdk", "changemac", "trunk"}
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"}
chipsetValues = []string{"i440fx", "Q35"}
loaderTypeValues = []string{"linux", "windows", "unknown"}
sepTypeValues = []string{"hitachi", "dorado", "tatlin", "shared", "local", "des"}
languageValues = []string{"ru", "en"}
userProviders = []string{"bvs", "decs3o"}
deviceValues = []string{"primary", "secondary"}
securityGroupDirectionValues = []string{"inbound", "outbound"}
securityGroupEthertypeValues = []string{"IPv4", "IPv6"}
securityGroupProtocolValues = []string{"icmp", "tcp", "udp"}
addressPoolNetTypeValues = []string{"IPv4", "IPv6", "MAC"}
ipTypeValues = []string{"v4, v6"}
sepTechStatusValues = []string{"ENABLED", "DISABLED"}
)
const (
mtuMin = 1
mtuMax = 9216
trunkTagsMin = 1
trunkTagsMax = 4095
) )

View File

@@ -1,23 +1,33 @@
package decortsdk package decortsdk
import ( import (
"bytes"
"context" "context"
"errors" "crypto/tls"
"encoding/json"
"fmt"
"io" "io"
"net/http" "net/http"
"net/url"
"strings" "strings"
"sync"
"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/client" "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"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker" "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/sdn"
) )
// 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
cfg config.LegacyConfig
expiryTime time.Time
mutex *sync.Mutex
} }
// Legacy client builder // Legacy client builder
@@ -26,9 +36,25 @@ func NewLegacy(cfg config.LegacyConfig) *LegacyDecortClient {
cfg.Retries = 5 cfg.Retries = 5
} }
var expiryTime time.Time
if cfg.Token != "" {
expiryTime = time.Now().AddDate(0, 0, 1)
}
return &LegacyDecortClient{ return &LegacyDecortClient{
decortURL: cfg.DecortURL, decortURL: cfg.DecortURL,
client: client.NewLegacyHttpClient(cfg), client: &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
//nolint:gosec
InsecureSkipVerify: cfg.SSLSkipVerify,
},
},
},
cfg: trimLegacyConfig(&cfg),
expiryTime: expiryTime,
mutex: &sync.Mutex{},
} }
} }
@@ -42,33 +68,229 @@ func (ldc *LegacyDecortClient) CloudBroker() *cloudbroker.CloudBroker {
return cloudbroker.New(ldc) return cloudbroker.New(ldc)
} }
// SDN builder
func (ldc *LegacyDecortClient) SDN() *sdn.SDN {
return sdn.New(ldc)
}
// 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) {
values, err := query.Values(params) // get token
if err := ldc.getToken(ctx); err != nil {
return nil, err
}
var body *bytes.Buffer
var ctype string
byteSlice, ok := params.([]byte)
if ok {
body = bytes.NewBuffer(byteSlice)
ctype = "application/octet-stream"
} else {
values, err := query.Values(params)
if err != nil {
return nil, err
}
body = bytes.NewBufferString(values.Encode() + fmt.Sprintf("&authkey=%s", ldc.cfg.Token))
}
req, err := http.NewRequestWithContext(ctx, method, ldc.decortURL+constants.RESTMACHINE+url, body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
body := strings.NewReader(values.Encode()) // perform request
req, err := http.NewRequestWithContext(ctx, method, ldc.decortURL+"/restmachine"+url, body) respBytes, err := ldc.do(req, ctype)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return respBytes, err
}
// DecortApiCallCtype method for sending requests to the platform with content type
func (ldc *LegacyDecortClient) DecortApiCallCtype(ctx context.Context, method, url, ctype string, params interface{}) ([]byte, error) {
// get token
if err := ldc.getToken(ctx); err != nil {
return nil, err
}
var body *bytes.Buffer
switch ctype {
case constants.MIMESTREAM:
body = bytes.NewBuffer(params.([]byte))
case constants.MIMEJSON:
jsonBody, err := json.Marshal(params)
if err != nil {
return nil, err
}
body = bytes.NewBuffer(jsonBody)
default:
ctype = constants.MIMEPOSTForm
values, err := query.Values(params)
if err != nil {
return nil, err
}
body = bytes.NewBufferString(values.Encode() + fmt.Sprintf("&authkey=%s", ldc.cfg.Token))
}
req, err := http.NewRequestWithContext(ctx, method, ldc.decortURL+constants.RESTMACHINE+url, body)
if 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) 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, 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 {
ldc.mutex.Lock()
defer ldc.mutex.Unlock()
// new token is not needed
if ldc.cfg.Token != "" && !time.Now().After(ldc.expiryTime) {
return nil
}
// 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) resp, err := ldc.client.Do(req)
if err != nil { if err != nil || resp == nil {
return nil, err return fmt.Errorf("cannot get token: %w", err)
} }
defer resp.Body.Close() defer resp.Body.Close()
respBytes, err := io.ReadAll(resp.Body) 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
}
// 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 != "" {
req.Header.Set("Content-Type", ctype)
}
req.Header.Set("Accept", "application/json")
buf, err := io.ReadAll(req.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if resp.StatusCode != 200 { req.Body.Close()
return nil, errors.New(string(respBytes)) req.Body = io.NopCloser(bytes.NewBuffer(buf))
resp, err := ldc.client.Do(req)
if resp != nil {
defer resp.Body.Close()
} }
return respBytes, nil // 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)
}
func trimLegacyConfig(cfg *config.LegacyConfig) config.LegacyConfig {
cfg.DecortURL = strings.TrimSuffix(cfg.DecortURL, "/")
return *cfg
} }

View File

@@ -8,7 +8,7 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for adding permission to access to account for a user // AddUserRequest struct to add permission to access account for a user
type AddUserRequest struct { type AddUserRequest struct {
// ID of account to add to // ID of account to add to
// Required: true // Required: true
@@ -30,9 +30,7 @@ type AddUserRequest struct {
func (a Account) AddUser(ctx context.Context, req AddUserRequest) (bool, error) { func (a Account) AddUser(ctx context.Context, req AddUserRequest) (bool, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return false, validators.ValidationErrors(validators.GetErrors(err))
return false, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/account/addUser" url := "/cloudapi/account/addUser"

View File

@@ -8,7 +8,7 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for give list account audits // AuditsRequest struct to give list of account audits
type AuditsRequest struct { type AuditsRequest struct {
// ID of the account // ID of the account
// Required: true // Required: true
@@ -19,9 +19,7 @@ type AuditsRequest struct {
func (a Account) Audits(ctx context.Context, req AuditsRequest) (ListAudits, error) { func (a Account) Audits(ctx context.Context, req AuditsRequest) (ListAudits, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return nil, validators.ValidationErrors(validators.GetErrors(err))
return nil, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/account/audits" url := "/cloudapi/account/audits"

View File

@@ -1,77 +0,0 @@
package account
import (
"context"
"net/http"
"strconv"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// Request struct for creating account
type CreateRequest struct {
// Display name
// Required: true
Name string `url:"name" json:"name" validate:"required"`
// Name of the account
// Required: true
Username string `url:"username" json:"username" validate:"required"`
// Email
// Required: false
EmailAddress string `url:"emailaddress,omitempty" json:"emailaddress,omitempty" validate:"omitempty,email"`
// Max size of memory in MB
// Required: false
MaxMemoryCapacity int64 `url:"maxMemoryCapacity,omitempty" json:"maxMemoryCapacity,omitempty"`
// Max size of aggregated vdisks in GB
// Required: false
MaxVDiskCapacity int64 `url:"maxVDiskCapacity,omitempty" json:"maxVDiskCapacity,omitempty"`
// Max number of CPU cores
// Required: false
MaxCPUCapacity int64 `url:"maxCPUCapacity,omitempty" json:"maxCPUCapacity,omitempty"`
// Max sent/received network transfer peering
// Required: false
MaxNetworkPeerTransfer int64 `url:"maxNetworkPeerTransfer,omitempty" json:"maxNetworkPeerTransfer,omitempty"`
// Max number of assigned public IPs
// Required: false
MaxNumPublicIP int64 `url:"maxNumPublicIP,omitempty" json:"maxNumPublicIP,omitempty"`
// If true send emails when a user is granted access to resources
// Required: false
SendAccessEmails bool `url:"sendAccessEmails,omitempty" json:"sendAccessEmails,omitempty"`
// Limit (positive) or disable (0) GPU resources
// Required: false
GPUUnits int64 `url:"gpu_units,omitempty" json:"gpu_units,omitempty"`
}
// Create creates account
// Setting a cloud unit maximum to -1 or empty will not put any restrictions on the resource
func (a Account) Create(ctx context.Context, req CreateRequest) (uint64, error) {
err := validators.ValidateRequest(req)
if err != nil {
for _, validationError := range validators.GetErrors(err) {
return 0, validators.ValidationError(validationError)
}
}
url := "/cloudapi/account/create"
res, err := a.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

@@ -7,7 +7,7 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for delete account // DeleteRequest struct to delete account
type DeleteRequest struct { type DeleteRequest struct {
// ID of account to delete // ID of account to delete
// Required: true // Required: true
@@ -19,20 +19,18 @@ type DeleteRequest struct {
} }
// Delete completes delete an account from the system Returns true if account is deleted or was already deleted or never existed // Delete completes delete an account from the system Returns true if account is deleted or was already deleted or never existed
func (a Account) Delete(ctx context.Context, req DeleteRequest) (bool, error) { func (a Account) Delete(ctx context.Context, req DeleteRequest) (string, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return "", validators.ValidationErrors(validators.GetErrors(err))
return false, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/account/delete" url := "/cloudapi/account/delete"
_, err = a.client.DecortApiCall(ctx, http.MethodPost, url, req) result, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil { if err != nil {
return false, err return "", err
} }
return true, nil return string(result), nil
} }

View File

@@ -8,7 +8,7 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for revoke access to account // DeleteUserRequest struct to revoke access to account
type DeleteUserRequest struct { type DeleteUserRequest struct {
// ID of the account // ID of the account
// Required: true // Required: true
@@ -17,19 +17,13 @@ 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,omitempty" json:"recursivedelete,omitempty"`
} }
// DeleteUser revokes user access from the account // DeleteUser revokes user access from the account
func (a Account) DeleteUser(ctx context.Context, req DeleteUserRequest) (bool, error) { func (a Account) DeleteUser(ctx context.Context, req DeleteUserRequest) (bool, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return false, validators.ValidationErrors(validators.GetErrors(err))
return false, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/account/deleteUser" url := "/cloudapi/account/deleteUser"

View File

@@ -8,7 +8,7 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for change status of account // DisableEnableRequest struct to change status of account
type DisableEnableRequest struct { type DisableEnableRequest struct {
// ID of account // ID of account
// Required: true // Required: true
@@ -19,9 +19,7 @@ type DisableEnableRequest struct {
func (a Account) Disable(ctx context.Context, req DisableEnableRequest) (bool, error) { func (a Account) Disable(ctx context.Context, req DisableEnableRequest) (bool, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return false, validators.ValidationErrors(validators.GetErrors(err))
return false, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/account/disable" url := "/cloudapi/account/disable"
@@ -43,9 +41,7 @@ func (a Account) Disable(ctx context.Context, req DisableEnableRequest) (bool, e
func (a Account) Enable(ctx context.Context, req DisableEnableRequest) (bool, error) { func (a Account) Enable(ctx context.Context, req DisableEnableRequest) (bool, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return false, validators.ValidationErrors(validators.GetErrors(err))
return false, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/account/enable" url := "/cloudapi/account/enable"

View File

@@ -48,21 +48,23 @@ func (la ListAccounts) FilterByUserGroupID(userGroupID string) ListAccounts {
func (la ListAccounts) FilterFunc(predicate func(ItemAccount) bool) ListAccounts { func (la ListAccounts) FilterFunc(predicate func(ItemAccount) bool) ListAccounts {
var result ListAccounts var result ListAccounts
for _, acc := range la { for _, acc := range la.Data {
if predicate(acc) { if predicate(acc) {
result = append(result, acc) result.Data = append(result.Data, acc)
} }
} }
result.EntryCount = uint64(len(result.Data))
return result return result
} }
// FindOne returns first found ItemAccount. // FindOne returns first found ItemAccount.
// If none was found, returns an empty struct. // If none was found, returns an empty struct.
func (la ListAccounts) FindOne() ItemAccount { func (la ListAccounts) FindOne() ItemAccount {
if len(la) == 0 { if len(la.Data) == 0 {
return ItemAccount{} return ItemAccount{}
} }
return la[0] return la.Data[0]
} }

View File

@@ -5,68 +5,71 @@ import (
) )
var accounts = ListAccounts{ var accounts = ListAccounts{
ItemAccount{ Data: []ItemAccount{
ACL: []RecordACL{ {
{ ACL: []ListRecordACL{
IsExplicit: true, {
GUID: "", IsExplicit: true,
Rights: "CXDRAU", GUID: "",
Status: "CONFIRMED", Rights: "CXDRAU",
Type: "U", Status: "CONFIRMED",
UgroupID: "timofey_tkachev_1@decs3o", Type: "U",
UgroupID: "timofey_tkachev_1@decs3o",
},
}, },
CreatedTime: 1676645275,
DeletedTime: 0,
ID: 132846,
Name: "std",
Status: "CONFIRMED",
UpdatedTime: 1676645275,
}, },
CreatedTime: 1676645275, {
DeletedTime: 0, ACL: []ListRecordACL{
ID: 132846, {
Name: "std", IsExplicit: true,
Status: "CONFIRMED", GUID: "",
UpdatedTime: 1676645275, Rights: "CXDRAU",
}, Status: "CONFIRMED",
ItemAccount{ Type: "U",
ACL: []RecordACL{ UgroupID: "not_really_timofey_tkachev_1@decs3o",
{ },
IsExplicit: true,
GUID: "",
Rights: "CXDRAU",
Status: "CONFIRMED",
Type: "U",
UgroupID: "not_really_timofey_tkachev_1@decs3o",
}, },
CreatedTime: 1676878820,
DeletedTime: 0,
ID: 132847,
Name: "std_2",
Status: "CONFIRMED",
UpdatedTime: 1676645275,
}, },
CreatedTime: 1676878820, {
DeletedTime: 0, ACL: []ListRecordACL{
ID: 132847, {
Name: "std_2", IsExplicit: true,
Status: "CONFIRMED", GUID: "",
UpdatedTime: 1676645275, Rights: "CXDRAU",
}, Status: "CONFIRMED",
ItemAccount{ Type: "U",
ACL: []RecordACL{ UgroupID: "timofey_tkachev_1@decs3o",
{ },
IsExplicit: true, {
GUID: "", IsExplicit: true,
Rights: "CXDRAU", GUID: "",
Status: "CONFIRMED", Rights: "CXDRAU",
Type: "U", Status: "CONFIRMED",
UgroupID: "timofey_tkachev_1@decs3o", Type: "U",
}, UgroupID: "second_account@decs3o",
{ },
IsExplicit: true,
GUID: "",
Rights: "CXDRAU",
Status: "CONFIRMED",
Type: "U",
UgroupID: "second_account@decs3o",
}, },
CreatedTime: 1676883850,
DeletedTime: 1676883899,
ID: 132848,
Name: "std_broker",
Status: "DELETED",
UpdatedTime: 1676878820,
}, },
CreatedTime: 1676883850,
DeletedTime: 1676883899,
ID: 132848,
Name: "std_broker",
Status: "DELETED",
UpdatedTime: 1676878820,
}, },
EntryCount: 3,
} }
func TestFilterByID(t *testing.T) { func TestFilterByID(t *testing.T) {
@@ -100,11 +103,11 @@ func TestFilterByName(t *testing.T) {
func TestFilterByStatus(t *testing.T) { func TestFilterByStatus(t *testing.T) {
actual := accounts.FilterByStatus("CONFIRMED") actual := accounts.FilterByStatus("CONFIRMED")
if len(actual) != 2 { if len(actual.Data) != 2 {
t.Fatal("Expected 2 elements in slice, found: ", len(actual)) t.Fatal("Expected 2 elements in slice, found: ", len(actual.Data))
} }
for _, item := range actual { for _, item := range actual.Data {
if item.Status != "CONFIRMED" { if item.Status != "CONFIRMED" {
t.Fatal("expected CONFIRMED, found: ", item.Status) t.Fatal("expected CONFIRMED, found: ", item.Status)
} }
@@ -116,7 +119,7 @@ func TestFilterFunc(t *testing.T) {
return ia.DeletedTime == 0 return ia.DeletedTime == 0
}) })
for _, item := range actual { for _, item := range actual.Data {
if item.DeletedTime != 0 { if item.DeletedTime != 0 {
t.Fatal("Expected DeletedTime = 0, found: ", item.DeletedTime) t.Fatal("Expected DeletedTime = 0, found: ", item.DeletedTime)
} }
@@ -126,21 +129,21 @@ func TestFilterFunc(t *testing.T) {
func TestSortingByCreatedTime(t *testing.T) { func TestSortingByCreatedTime(t *testing.T) {
actual := accounts.SortByCreatedTime(false) actual := accounts.SortByCreatedTime(false)
if actual[0].Name != "std" { if actual.Data[0].Name != "std" {
t.Fatal("Expected account std as earliest, found: ", actual[0].Name) t.Fatal("Expected account std as earliest, found: ", actual.Data[0].Name)
} }
actual = accounts.SortByCreatedTime(true) actual = accounts.SortByCreatedTime(true)
if actual[0].Name != "std_broker" { if actual.Data[0].Name != "std_broker" {
t.Fatal("Expected account std_broker as latest, found: ", actual[0].Name) t.Fatal("Expected account std_broker as latest, found: ", actual.Data[0].Name)
} }
} }
func TestFilterEmpty(t *testing.T) { func TestFilterEmpty(t *testing.T) {
actual := accounts.FilterByID(0) actual := accounts.FilterByID(0)
if len(actual) != 0 { if len(actual.Data) != 0 {
t.Fatal("Expected 0 found, actual: ", len(actual)) t.Fatal("Expected 0 found, actual: ", len(actual.Data))
} }
} }

View File

@@ -8,25 +8,16 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for get information about account // GetRequest struct to get information about account
type GetRequest struct { type GetRequest struct {
// ID an account // ID an account
// Required: true // Required: true
AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` AccountID uint64 `url:"accountId" json:"accountId" validate:"required"`
} }
// Get gets account details // Get gets account details as a RecordAccount struct
func (a Account) Get(ctx context.Context, req GetRequest) (*RecordAccount, error) { func (a Account) Get(ctx context.Context, req GetRequest) (*RecordAccount, error) {
err := validators.ValidateRequest(req) res, err := a.GetRaw(ctx, req)
if err != nil {
for _, validationError := range validators.GetErrors(err) {
return nil, validators.ValidationError(validationError)
}
}
url := "/cloudapi/account/get"
res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -41,3 +32,16 @@ func (a Account) Get(ctx context.Context, req GetRequest) (*RecordAccount, error
return &info, nil return &info, nil
} }
// GetRaw gets account details as an array of bytes
func (a Account) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) {
err := validators.ValidateRequest(req)
if err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/account/get"
res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req)
return res, err
}

View File

@@ -8,7 +8,7 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for calculate the currently consumed units for all cloudspaces and resource groups in the account // GetConsumedAccountUnitsRequest struct to calculate the currently consumed units for all cloudspaces and resource groups in the account
type GetConsumedAccountUnitsRequest struct { type GetConsumedAccountUnitsRequest struct {
// ID an account // ID an account
// Required: true // Required: true
@@ -20,13 +20,12 @@ type GetConsumedAccountUnitsRequest struct {
// - CU_M: consumed memory in MB // - CU_M: consumed memory in MB
// - CU_C: number of cpu cores // - CU_C: number of cpu cores
// - CU_D: consumed vdisk storage in GB // - CU_D: consumed vdisk storage in GB
// - CU_DM: consumed max vdisk storage in GB
// - CU_I: number of public IPs // - CU_I: number of public IPs
func (a Account) GetConsumedAccountUnits(ctx context.Context, req GetConsumedAccountUnitsRequest) (*ResourceLimits, error) { func (a Account) GetConsumedAccountUnits(ctx context.Context, req GetConsumedAccountUnitsRequest) (*ResourceLimits, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return nil, validators.ValidationErrors(validators.GetErrors(err))
return nil, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/account/getConsumedAccountUnits" url := "/cloudapi/account/getConsumedAccountUnits"

View File

@@ -8,7 +8,7 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for calculate the currently consumed cloud units of the specified type for all cloudspaces and resource groups in the account // GetConsumedCloudUnitsByTypeRequest struct to calculate the currently consumed cloud units of the specified type for all cloudspaces and resource groups in the account
type GetConsumedCloudUnitsByTypeRequest struct { type GetConsumedCloudUnitsByTypeRequest struct {
// ID an account // ID an account
// Required: true // Required: true
@@ -26,17 +26,13 @@ type GetConsumedCloudUnitsByTypeRequest struct {
// - CU_M: returns consumed memory in MB // - CU_M: returns consumed memory in MB
// - CU_C: returns number of virtual cpu cores // - CU_C: returns number of virtual cpu cores
// - CU_D: returns consumed virtual disk storage in GB // - CU_D: returns consumed virtual disk storage in GB
// - CU_S: returns consumed primary storage (NAS) in TB // - CU_DM: returns consumed max virtual disk storage in GB
// - CU_A: returns consumed secondary storage (Archive) in TB
// - CU_NO: returns sent/received network transfer in operator in GB
// - CU_NP: returns sent/received network transfer peering in GB
// - CU_I: returns number of public IPs // - CU_I: returns number of public IPs
// - gpu_units: return number of GPU units
func (a Account) GetConsumedCloudUnitsByType(ctx context.Context, req GetConsumedCloudUnitsByTypeRequest) (float64, error) { func (a Account) GetConsumedCloudUnitsByType(ctx context.Context, req GetConsumedCloudUnitsByTypeRequest) (float64, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return 0, validators.ValidationErrors(validators.GetErrors(err))
return 0, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/account/getConsumedCloudUnitsByType" url := "/cloudapi/account/getConsumedCloudUnitsByType"

View File

@@ -1,62 +0,0 @@
package account
import (
"context"
"net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// Request struct for 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 {
for _, validationError := range validators.GetErrors(err) {
return "", validators.ValidationError(validationError)
}
}
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 {
for _, validationError := range validators.GetErrors(err) {
return "", validators.ValidationError(validationError)
}
}
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

@@ -8,7 +8,7 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for calculate the reserved units for all cloudspaces and resource groups in the account // GetReservedAccountUnitsRequest struct to calculate the reserved units for all cloudspaces and resource groups in the account
type GetReservedAccountUnitsRequest struct { type GetReservedAccountUnitsRequest struct {
// ID an account // ID an account
// Required: true // Required: true
@@ -21,13 +21,12 @@ type GetReservedAccountUnitsRequest struct {
// - CU_M: reserved memory in MB // - CU_M: reserved memory in MB
// - CU_C: number of cpu cores // - CU_C: number of cpu cores
// - CU_D: reserved vdisk storage in GB // - CU_D: reserved vdisk storage in GB
// - CU_DM: reserved max vdisk storage in GB
// - CU_I: number of public IPs // - CU_I: number of public IPs
func (a Account) GetReservedAccountUnits(ctx context.Context, req GetReservedAccountUnitsRequest) (*ResourceLimits, error) { func (a Account) GetReservedAccountUnits(ctx context.Context, req GetReservedAccountUnitsRequest) (*ResourceLimits, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return nil, validators.ValidationErrors(validators.GetErrors(err))
return nil, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/account/getReservedAccountUnits" url := "/cloudapi/account/getReservedAccountUnits"

View File

@@ -0,0 +1,40 @@
package account
import (
"context"
"encoding/json"
"net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// GetResourceConsumptionRequest struct to get resource consumption
type GetResourceConsumptionRequest struct {
// ID an account
// Required: true
AccountID uint64 `url:"accountId" json:"accountId" validate:"required"`
}
// GetResourceConsumption show amount of consumed and reserved resources (cpu, ram, disk) by specific account
func (a Account) GetResourceConsumption(ctx context.Context, req GetResourceConsumptionRequest) (*RecordResourceConsumption, error) {
err := validators.ValidateRequest(req)
if err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/account/getResourceConsumption"
info := RecordResourceConsumption{}
res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil {
return nil, err
}
err = json.Unmarshal(res, &info)
if err != nil {
return nil, err
}
return &info, nil
}

View File

@@ -0,0 +1,73 @@
package account
// IDs gets array of AccountIDs from ListAccounts struct
func (la ListAccounts) IDs() []uint64 {
res := make([]uint64, 0, len(la.Data))
for _, acc := range la.Data {
res = append(res, acc.ID)
}
return res
}
// IDs gets array of ComputeIDs from ListComputes struct
func (lc ListComputes) IDs() []uint64 {
res := make([]uint64, 0, len(lc.Data))
for _, c := range lc.Data {
res = append(res, c.ComputeID)
}
return res
}
// IDs gets array of DiskIDs from ListDisks struct
func (ld ListDisks) IDs() []uint64 {
res := make([]uint64, 0, len(ld.Data))
for _, d := range ld.Data {
res = append(res, d.ID)
}
return res
}
// IDs gets array of FLIPGroupIDs from ListFLIPGroups struct
func (fg ListFLIPGroups) IDs() []uint64 {
res := make([]uint64, 0, len(fg.Data))
for _, g := range fg.Data {
res = append(res, g.ID)
}
return res
}
// IDs gets array of AccountIDs from ListResourceConsumption struct
func (rc ListResourceConsumption) IDs() []uint64 {
res := make([]uint64, 0, len(rc.Data))
for _, r := range rc.Data {
res = append(res, r.AccountID)
}
return res
}
// IDs gets array of RGIDs from ListRG struct
func (rg ListRG) IDs() []uint64 {
res := make([]uint64, 0, len(rg.Data))
for _, g := range rg.Data {
res = append(res, g.RGID)
}
return res
}
// IDs gets array of TemplateIDs from ListTemplates struct
func (lt ListTemplates) IDs() []uint64 {
res := make([]uint64, 0, len(lt.Data))
for _, t := range lt.Data {
res = append(res, t.ID)
}
return res
}
// IDs gets array of VINSIDs from ListVINS struct
func (lv ListVINS) IDs() []uint64 {
res := make([]uint64, 0, len(lv.Data))
for _, v := range lv.Data {
res = append(res, v.ID)
}
return res
}

View File

@@ -4,24 +4,50 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"net/http" "net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for get list of accounts // ListRequest struct to get list of accounts
type ListRequest struct { type ListRequest struct {
// Find by ID
// Required: false
ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"`
// Find by name
// Required: false
Name string `url:"name,omitempty" json:"name,omitempty"`
// Find by access control list
// Required: false
ACL string `url:"acl,omitempty" json:"acl,omitempty"`
// Find by status
// Required: false
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"`
// Sort by zone id
// Default value: 0
// Required: false
ZoneID uint64 `url:"zone_id,omitempty" json:"zone_id,omitempty"`
// Page number // Page number
// Required: false // Required: false
Page uint64 `url:"page" json:"page"` Page uint64 `url:"page,omitempty" json:"page,omitempty"`
// Page size // Page size
// Required: false // Required: false
Size uint64 `url:"size" json:"size"` Size uint64 `url:"size,omitempty" json:"size,omitempty"`
} }
// List gets list all accounts the user has access to // 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) {
url := "/cloudapi/account/list"
res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) res, err := a.ListRaw(ctx, req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -33,5 +59,18 @@ func (a Account) List(ctx context.Context, req ListRequest) (ListAccounts, error
return nil, err return nil, err
} }
return list, nil return &list, nil
}
// 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) {
if err := validators.ValidateRequest(req); err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/account/list"
res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req)
return res, err
} }

View File

@@ -8,20 +8,63 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for a get list compute instances // ListComputesRequest struct to get a list of compute instances
type ListComputesRequest struct { type ListComputesRequest struct {
// ID an account // ID an account
// Required: true // Required: true
AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` AccountID uint64 `url:"accountId" json:"accountId" validate:"required"`
// Find by compute id
// Required: false
ComputeID uint64 `url:"computeId,omitempty" json:"computeId,omitempty"`
// Find by name
// Required: false
Name string `url:"name,omitempty" json:"name,omitempty"`
// Find by resource group name
// Required: false
RGName string `url:"rgName,omitempty" json:"rgName,omitempty"`
// Find by resource group id
// Required: false
RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"`
// Find by tech status
// Required: false
TechStatus string `url:"techStatus,omitempty" json:"techStatus,omitempty"`
// Find by ip address
// Required: false
IPAddress string `url:"ipAddress,omitempty" json:"ipAddress,omitempty"`
// Find by external network name
// Required: false
ExtNetName string `url:"extNetName,omitempty" json:"extNetName,omitempty"`
// Find by external network id
// Required: false
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
// Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"`
// Page size
// Required: false
Size uint64 `url:"size,omitempty" json:"size,omitempty"`
} }
// 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 {
for _, validationError := range validators.GetErrors(err) { return nil, validators.ValidationErrors(validators.GetErrors(err))
return nil, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/account/listComputes" url := "/cloudapi/account/listComputes"
@@ -38,5 +81,5 @@ func (a Account) ListComputes(ctx context.Context, req ListComputesRequest) (Lis
return nil, err return nil, err
} }
return list, nil return &list, nil
} }

View File

@@ -4,21 +4,45 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"net/http" "net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for get list deleted accounts // ListDeletedRequest struct to get a list of deleted accounts
type ListDeletedRequest struct { type ListDeletedRequest struct {
// Page number // Page number
// Required: false // Required: false
Page uint64 `url:"page" json:"page"` Page uint64 `url:"page,omitempty" json:"page,omitempty"`
// Page size // Page size
// Required: false // Required: false
Size uint64 `url:"size" json:"size"` Size uint64 `url:"size,omitempty" json:"size,omitempty"`
// Find by ID
// Required: false
ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"`
// Find by name
// Required: false
Name string `url:"name,omitempty" json:"name,omitempty"`
// Find by access control list
// Required: false
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 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)
@@ -33,5 +57,5 @@ func (a Account) ListDeleted(ctx context.Context, req ListDeletedRequest) (ListA
return nil, err return nil, err
} }
return list, nil return &list, nil
} }

View File

@@ -8,20 +8,47 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for get list deleted disks // ListDisksRequest struct to get a list of deleted disks
type ListDisksRequest struct { type ListDisksRequest struct {
// ID an account // ID an account
// Required: true // Required: true
AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` AccountID uint64 `url:"accountId" json:"accountId" validate:"required"`
// Find by disk id
// Required: false
DiskID uint64 `url:"diskId,omitempty" json:"diskId,omitempty"`
// Find by name
// Required: false
Name string `url:"name,omitempty" json:"name,omitempty"`
// Find by max size disk
// Required: false
DiskMaxSize uint64 `url:"diskMaxSize,omitempty" json:"diskMaxSize,omitempty"`
// Type of the disks
// Required: false
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
// Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"`
// Page size
// Required: false
Size uint64 `url:"size,omitempty" json:"size,omitempty"`
} }
// 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 {
for _, validationError := range validators.GetErrors(err) { return nil, validators.ValidationErrors(validators.GetErrors(err))
return nil, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/account/listDisks" url := "/cloudapi/account/listDisks"
@@ -38,5 +65,5 @@ func (a Account) ListDisks(ctx context.Context, req ListDisksRequest) (ListDisks
return nil, err return nil, err
} }
return list, nil return &list, nil
} }

View File

@@ -8,20 +8,55 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for get list FLIPGroups // ListFLIPGroupsRequest struct to get a list of FLIPGroups
type ListFLIPGroupsRequest struct { type ListFLIPGroupsRequest struct {
// ID an account // ID of the account
// Required: true // Required: true
AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` AccountID uint64 `url:"accountId" json:"accountId" validate:"required"`
// Find by name
// Required: false
Name string `url:"name,omitempty" json:"name,omitempty"`
// Find by vinsId
// Required: false
VINSID uint64 `url:"vinsId,omitempty" json:"vinsId,omitempty"`
// Find by VINS name
// Required: false
VINSName string `url:"vinsName,omitempty" json:"vinsName,omitempty"`
// Find by external network id
// Required: false
ExtNetID uint64 `url:"extnetId,omitempty" json:"extnetId,omitempty"`
// Find by IP
// Required: false
ByIP string `url:"byIp,omitempty" json:"byIp,omitempty"`
// Find by flipGroup Id
// Required: false
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
// Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"`
// Page size
// Required: false
Size uint64 `url:"size,omitempty" json:"size,omitempty"`
} }
// 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 {
for _, validationError := range validators.GetErrors(err) { return nil, validators.ValidationErrors(validators.GetErrors(err))
return nil, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/account/listFlipGroups" url := "/cloudapi/account/listFlipGroups"
@@ -38,5 +73,5 @@ func (a Account) ListFLIPGroups(ctx context.Context, req ListFLIPGroupsRequest)
return nil, err return nil, err
} }
return list, nil return &list, nil
} }

View File

@@ -0,0 +1,26 @@
package account
import (
"context"
"encoding/json"
"net/http"
)
// ListResourceConsumption show data list amount of consumed and reserved resources (cpu, ram, disk) by specific accounts
func (a Account) ListResourceConsumption(ctx context.Context) (*ListResourceConsumption, error) {
url := "/cloudapi/account/listResourceConsumption"
info := ListResourceConsumption{}
res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, nil)
if err != nil {
return nil, err
}
err = json.Unmarshal(res, &info)
if err != nil {
return nil, err
}
return &info, nil
}

View File

@@ -8,20 +8,51 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for get list resource groups // ListRGRequest struct to get a list of resource groups
type ListRGRequest struct { type ListRGRequest struct {
// ID an account // ID an account
// Required: true // Required: true
AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` AccountID uint64 `url:"accountId" json:"accountId" validate:"required"`
// Page number
// Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"`
// Page size
// Required: false
Size uint64 `url:"size,omitempty" json:"size,omitempty"`
// Find by resource group id
// Required: false
RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"`
// Find by name
// Required: false
Name string `url:"name,omitempty" json:"name,omitempty"`
// Find by vinsId
// Required: false
VINSID uint64 `url:"vinsId,omitempty" json:"vinsId,omitempty"`
// Find by VM ID
// Required: false
VMID uint64 `url:"vmId,omitempty" json:"vmId,omitempty"`
// Find by status
// Required: false
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 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 {
for _, validationError := range validators.GetErrors(err) { return nil, validators.ValidationErrors(validators.GetErrors(err))
return nil, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/account/listRG" url := "/cloudapi/account/listRG"
@@ -38,5 +69,5 @@ func (a Account) ListRG(ctx context.Context, req ListRGRequest) (ListRG, error)
return nil, err return nil, err
} }
return list, nil return &list, nil
} }

View File

@@ -8,24 +8,47 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for get list templates // ListTemplatesRequest struct to get a list of templates
type ListTemplatesRequest struct { type ListTemplatesRequest struct {
// ID an account // ID an account
// Required: true // Required: true
AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` AccountID uint64 `url:"accountId" json:"accountId" validate:"required"`
// Include deleted images // Include deleted images
// Required: false // Required: false
IncludeDeleted bool `url:"includedeleted" json:"includedeleted"` IncludeDeleted bool `url:"includedeleted,omitempty" json:"includedeleted,omitempty"`
// Find by image id
// Required: false
ImageID uint64 `url:"imageId,omitempty" json:"imageId,omitempty"`
// Find by name
// Required: false
Name string `url:"name,omitempty" json:"name,omitempty"`
// Find by type
// Required: false
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
// Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"`
// Page size
// Required: false
Size uint64 `url:"size,omitempty" json:"size,omitempty"`
} }
// ListTemplates gets list 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 {
for _, validationError := range validators.GetErrors(err) { return nil, validators.ValidationErrors(validators.GetErrors(err))
return nil, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/account/listTemplates" url := "/cloudapi/account/listTemplates"
@@ -42,5 +65,5 @@ func (a Account) ListTemplates(ctx context.Context, req ListTemplatesRequest) (L
return nil, err return nil, err
} }
return list, nil return &list, nil
} }

View File

@@ -8,20 +8,47 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for get list VINS // ListVINSRequest struct to get a list of VINS
type ListVINSRequest struct { type ListVINSRequest struct {
// ID an account // ID an account
// Required: true // Required: true
AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` AccountID uint64 `url:"accountId" json:"accountId" validate:"required"`
// Find by VINS ID
// Required: false
VINSID uint64 `url:"vins,omitempty" json:"vinsId,omitempty"`
// Find by name
// Required: false
Name string `url:"name,omitempty" json:"name,omitempty"`
// Find by resource group id
// Required: false
RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"`
// Find by external network ip
// Required: false
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
// Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"`
// Page size
// Required: false
Size uint64 `url:"size,omitempty" json:"size,omitempty"`
} }
// ListVINS gets list 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 {
for _, validationError := range validators.GetErrors(err) { return nil, validators.ValidationErrors(validators.GetErrors(err))
return nil, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/account/listVins" url := "/cloudapi/account/listVins"
@@ -38,5 +65,5 @@ func (a Account) ListVINS(ctx context.Context, req ListVINSRequest) (ListVINS, e
return nil, err return nil, err
} }
return list, nil return &list, nil
} }

View File

@@ -1,7 +1,34 @@
package account package account
// Access Control List
type ListRecordACL struct {
// Whether access is explicitly specified
IsExplicit bool `json:"explicit"`
// GUID
GUID string `json:"guid"`
// Access rights
Rights string `json:"right"`
// Status
Status string `json:"status"`
// Account Type
Type string `json:"type"`
// Account owner ID
UgroupID string `json:"userGroupId"`
// Is it possible to remove
CanBeDeleted bool `json:"canBeDeleted"`
}
// Access Control List // Access Control List
type RecordACL struct { type RecordACL struct {
// Emails
Emails []string `json:"emails"`
// Whether access is explicitly specified // Whether access is explicitly specified
IsExplicit bool `json:"explicit"` IsExplicit bool `json:"explicit"`
@@ -32,23 +59,35 @@ type ResourceLimits struct {
// Disk size, GB // Disk size, GB
CUD float64 `json:"CU_D"` CUD float64 `json:"CU_D"`
// Max disk size, GB
CUDM float64 `json:"CU_DM"`
// Number of public IP addresses // Number of public IP addresses
CUI float64 `json:"CU_I"` CUI float64 `json:"CU_I"`
// RAM size, MB // RAM size, MB
CUM float64 `json:"CU_M"` CUM float64 `json:"CU_M"`
// Traffic volume, GB
CUNP float64 `json:"CU_NP"`
// Number of graphics cores // Number of graphics cores
GPUUnits float64 `json:"gpu_units"` GPUUnits float64 `json:"gpu_units"`
// Storage policy
StoragePolicy []StoragePolicyItem `json:"storage_policy"`
}
type StoragePolicyItem struct {
ID uint64 `json:"id"`
Limit int `json:"limit"`
} }
// Main information in one of if the list of accounts // Main information in one of if the list of accounts
type ItemAccount struct { type ItemAccount struct {
// Access Control List // Access Control List
ACL []RecordACL `json:"acl"` ACL []ListRecordACL `json:"acl"`
// Compute Features
ComputeFeatures []string `json:"computeFeatures"`
// Created time // Created time
CreatedTime uint64 `json:"createdTime"` CreatedTime uint64 `json:"createdTime"`
@@ -56,6 +95,9 @@ type ItemAccount struct {
// Deleted time // Deleted time
DeletedTime uint64 `json:"deletedTime"` DeletedTime uint64 `json:"deletedTime"`
// Description
Description string `json:"desc"`
// ID // ID
ID uint64 `json:"id"` ID uint64 `json:"id"`
@@ -67,10 +109,29 @@ type ItemAccount struct {
// Updated time // Updated time
UpdatedTime uint64 `json:"updatedTime"` UpdatedTime uint64 `json:"updatedTime"`
// Zones
ZoneIDs []uint64 `json:"zoneIds"`
} }
// List of accounts // List of accounts
type ListAccounts []ItemAccount type ListAccounts struct {
Data []ItemAccount `json:"data"`
EntryCount uint64 `json:"entryCount"`
}
// Policy
type Policy struct {
// Size of the disk
DiskSize float64 `json:"disksize"`
// Max size of the disk
DiskSizeMax float64 `json:"disksizemax"`
// SEPs used
SEPs map[string]map[string]DiskUsage `json:"seps"`
}
// Resources used // Resources used
type Resource struct { type Resource struct {
@@ -81,17 +142,17 @@ type Resource struct {
DiskSize float64 `json:"disksize"` DiskSize float64 `json:"disksize"`
// Max disk size // Max disk size
DiskSizeMax uint64 `json:"disksizemax"` DiskSizeMax float64 `json:"disksizemax"`
// Number of External IPs // Number of External IPs
ExtIPs int64 `json:"extips"` ExtIPs int64 `json:"extips"`
// External traffic
ExtTraffic int64 `json:"exttraffic"`
// Number of grafic cores // Number of grafic cores
GPU int64 `json:"gpu"` GPU int64 `json:"gpu"`
// Policies
Policies map[string]Policy `json:"policies"`
// Number of RAM // Number of RAM
RAM int64 `json:"ram"` RAM int64 `json:"ram"`
@@ -105,16 +166,33 @@ type DiskUsage struct {
DiskSize float64 `json:"disksize"` DiskSize float64 `json:"disksize"`
// Disk size max // Disk size max
DiskSizeMax uint64 `json:"disksizemax"` DiskSizeMax float64 `json:"disksizemax"`
}
// Information about resource consumption
type RecordResourceConsumption struct {
ItemResourceConsumption
// Resource limits
ResourceLimits ResourceLimits `json:"resourceLimits"`
} }
// Information about resources // Information about resources
type Resources struct { type ItemResourceConsumption struct {
// Current information about resources // Current information about resources
Current Resource `json:"Current"` Consumed Resource `json:"consumed"`
// Reserved information about resources // Reserved information about resources
Reserved Resource `json:"Reserved"` Reserved Resource `json:"reserved"`
// Account ID
AccountID uint64 `json:"id"`
}
type ListResourceConsumption struct {
Data []ItemResourceConsumption `json:"data"`
EntryCount uint64 `json:"entryCount"`
} }
// Information about computes // Information about computes
@@ -135,17 +213,20 @@ type Machines struct {
Halted uint64 `json:"halted"` Halted uint64 `json:"halted"`
} }
// Detailed information about the account zone
type ZoneID struct {
// ID of zone
ID int64 `json:"id"`
// Name of zone
Name string `json:"name"`
}
// Main information about account // Main information about account
type RecordAccount struct { type RecordAccount struct {
// DCLocation // DCLocation
DCLocation string `json:"DCLocation"` DCLocation string `json:"DCLocation"`
// Resources
Resources Resources `json:"Resources"`
// CKey
CKey string `json:"_ckey"`
// Access control list // Access control list
ACL []RecordACL `json:"acl"` ACL []RecordACL `json:"acl"`
@@ -155,6 +236,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"`
@@ -170,6 +254,9 @@ type RecordAccount struct {
// Created time // Created time
CreatedTime uint64 `json:"createdTime"` CreatedTime uint64 `json:"createdTime"`
// Description
Description string `json:"desc"`
// Deactivation time // Deactivation time
DeactivationTime float64 `json:"deactivationTime"` DeactivationTime float64 `json:"deactivationTime"`
@@ -206,6 +293,9 @@ type RecordAccount struct {
// Status // Status
Status string `json:"status"` Status string `json:"status"`
// Storage policy ids
StoragePolicyIDs []uint64 `json:"storage_policy_ids"`
// UniqPools // UniqPools
UniqPools []interface{} `json:"uniqPools"` UniqPools []interface{} `json:"uniqPools"`
@@ -220,6 +310,12 @@ type RecordAccount struct {
// VINSes // VINSes
VINSes uint64 `json:"vinses"` VINSes uint64 `json:"vinses"`
// Zone
ZoneIDs []ZoneID `json:"zoneIds"`
// Zones
DefaultZoneID uint64 `json:"defaultZoneId"`
} }
// Main information about compute // Main information about compute
@@ -286,7 +382,13 @@ type ItemCompute struct {
} }
// List of computes // List of computes
type ListComputes []ItemCompute type ListComputes struct {
// Data
Data []ItemCompute `json:"data"`
// Entry count
EntryCount uint64 `json:"entryCount"`
}
// Main information about disk // Main information about disk
type ItemDisk struct { type ItemDisk struct {
@@ -313,7 +415,13 @@ type ItemDisk struct {
} }
// List of disks // List of disks
type ListDisks []ItemDisk type ListDisks struct {
// Data
Data []ItemDisk `json:"data"`
// Entry count
EntryCount uint64 `json:"entryCount"`
}
// Main information about VINS // Main information about VINS
type ItemVINS struct { type ItemVINS struct {
@@ -341,6 +449,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"`
@@ -370,7 +484,13 @@ type ItemVINS struct {
} }
// List of VINS // List of VINS
type ListVINS []ItemVINS type ListVINS struct {
// Data
Data []ItemVINS `json:"data"`
// Entry count
EntryCount uint64 `json:"entryCount"`
}
// Main info about audit // Main info about audit
type ItemAudit struct { type ItemAudit struct {
@@ -428,9 +548,6 @@ type LimitsRG struct {
// Number of External IPs // Number of External IPs
ExtIPs int64 `json:"extips"` ExtIPs int64 `json:"extips"`
// External traffic
ExtTraffic int64 `json:"exttraffic"`
// Number of grafic cores // Number of grafic cores
GPU int64 `json:"gpu"` GPU int64 `json:"gpu"`
@@ -439,6 +556,9 @@ type LimitsRG struct {
// SEPs // SEPs
SEPs uint64 `json:"seps"` SEPs uint64 `json:"seps"`
// Policies
Policies map[string]Policy `json:"policies"`
} }
// Main information about resource group // Main information about resource group
@@ -461,6 +581,9 @@ type ItemRG struct {
// Deleted time // Deleted time
DeletedTime uint64 `json:"deletedTime"` DeletedTime uint64 `json:"deletedTime"`
// Description
Description string `json:"desc"`
// Resource group ID // Resource group ID
RGID uint64 `json:"id"` RGID uint64 `json:"id"`
@@ -484,7 +607,13 @@ type ItemRG struct {
} }
// List of Resource groups // List of Resource groups
type ListRG []ItemRG type ListRG struct {
// Data
Data []ItemRG `json:"data"`
// Enrtry count
EntryCount uint64 `json:"entryCount"`
}
// Main information about template // Main information about template
type ItemTemplate struct { type ItemTemplate struct {
@@ -520,7 +649,13 @@ type ItemTemplate struct {
} }
// List of templates // List of templates
type ListTemplates []ItemTemplate type ListTemplates struct {
// Data
Data []ItemTemplate `json:"data"`
// Entry count
EntryCount uint64 `json:"entryCount"`
}
// Main information about FLIPGroup // Main information about FLIPGroup
type ItemFLIPGroup struct { type ItemFLIPGroup struct {
@@ -589,4 +724,10 @@ type ItemFLIPGroup struct {
} }
// List of FLIPGroups // List of FLIPGroups
type ListFLIPGroups []ItemFLIPGroup type ListFLIPGroups struct {
// Data
Data []ItemFLIPGroup `json:"data"`
// Entry count
EntryCount uint64 `json:"entryCount"`
}

View File

@@ -3,10 +3,11 @@ package account
import ( import (
"context" "context"
"net/http" "net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for restore a deleted account // RestoreRequest struct to restore a deleted account
type RestoreRequest struct { type RestoreRequest struct {
// ID an account // ID an account
// Required: true // Required: true
@@ -14,20 +15,18 @@ type RestoreRequest struct {
} }
// Restore restores a deleted account // Restore restores a deleted account
func (a Account) Restore(ctx context.Context, req RestoreRequest) (bool, error) { func (a Account) Restore(ctx context.Context, req RestoreRequest) (string, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return "", validators.ValidationErrors(validators.GetErrors(err))
return false, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/account/restore" url := "/cloudapi/account/restore"
_, err = a.client.DecortApiCall(ctx, http.MethodPost, url, req) result, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil { if err != nil {
return false, err return "", err
} }
return true, nil return string(result), nil
} }

View File

@@ -12,7 +12,7 @@ import (
// - First argument -> prefix // - First argument -> prefix
// - Second argument -> indent // - Second argument -> indent
func (la ListAccounts) Serialize(params ...string) (serialization.Serialized, error) { func (la ListAccounts) Serialize(params ...string) (serialization.Serialized, error) {
if len(la) == 0 { if len(la.Data) == 0 {
return []byte{}, nil return []byte{}, nil
} }

View File

@@ -6,16 +6,16 @@ import "sort"
// //
// If inverse param is set to true, the order is reversed. // If inverse param is set to true, the order is reversed.
func (la ListAccounts) SortByCreatedTime(inverse bool) ListAccounts { func (la ListAccounts) SortByCreatedTime(inverse bool) ListAccounts {
if len(la) < 2 { if len(la.Data) < 2 {
return la return la
} }
sort.Slice(la, func(i, j int) bool { sort.Slice(la.Data, func(i, j int) bool {
if inverse { if inverse {
return la[i].CreatedTime > la[j].CreatedTime return la.Data[i].CreatedTime > la.Data[j].CreatedTime
} }
return la[i].CreatedTime < la[j].CreatedTime return la.Data[i].CreatedTime < la.Data[j].CreatedTime
}) })
return la return la
@@ -25,16 +25,16 @@ func (la ListAccounts) SortByCreatedTime(inverse bool) ListAccounts {
// //
// If inverse param is set to true, the order is reversed. // If inverse param is set to true, the order is reversed.
func (la ListAccounts) SortByUpdatedTime(inverse bool) ListAccounts { func (la ListAccounts) SortByUpdatedTime(inverse bool) ListAccounts {
if len(la) < 2 { if len(la.Data) < 2 {
return la return la
} }
sort.Slice(la, func(i, j int) bool { sort.Slice(la.Data, func(i, j int) bool {
if inverse { if inverse {
return la[i].UpdatedTime > la[j].UpdatedTime return la.Data[i].UpdatedTime > la.Data[j].UpdatedTime
} }
return la[i].UpdatedTime < la[j].UpdatedTime return la.Data[i].UpdatedTime < la.Data[j].UpdatedTime
}) })
return la return la
@@ -44,16 +44,16 @@ func (la ListAccounts) SortByUpdatedTime(inverse bool) ListAccounts {
// //
// If inverse param is set to true, the order is reversed. // If inverse param is set to true, the order is reversed.
func (la ListAccounts) SortByDeletedTime(inverse bool) ListAccounts { func (la ListAccounts) SortByDeletedTime(inverse bool) ListAccounts {
if len(la) < 2 { if len(la.Data) < 2 {
return la return la
} }
sort.Slice(la, func(i, j int) bool { sort.Slice(la.Data, func(i, j int) bool {
if inverse { if inverse {
return la[i].DeletedTime > la[j].DeletedTime return la.Data[i].DeletedTime > la.Data[j].DeletedTime
} }
return la[i].DeletedTime < la[j].DeletedTime return la.Data[i].DeletedTime < la.Data[j].DeletedTime
}) })
return la return la

View File

@@ -8,12 +8,16 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for updaate account // UpdateRequest struct to update account
type UpdateRequest struct { type UpdateRequest struct {
// ID an account // ID an account
// Required: true // Required: true
AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` AccountID uint64 `url:"accountId" json:"accountId" validate:"required"`
// Description
// Required: false
Description string `url:"desc,omitempty" json:"desc,omitempty"`
// Name of the account // Name of the account
// Required: false // Required: false
Name string `url:"name,omitempty" json:"name,omitempty"` Name string `url:"name,omitempty" json:"name,omitempty"`
@@ -30,30 +34,33 @@ type UpdateRequest struct {
// Required: false // Required: false
MaxCPUCapacity int64 `url:"maxCPUCapacity,omitempty" json:"maxCPUCapacity,omitempty"` MaxCPUCapacity int64 `url:"maxCPUCapacity,omitempty" json:"maxCPUCapacity,omitempty"`
// Max sent/received network transfer peering
// Required: false
MaxNetworkPeerTransfer int64 `url:"maxNetworkPeerTransfer,omitempty" json:"maxNetworkPeerTransfer,omitempty"`
// Max number of assigned public IPs // Max number of assigned public IPs
// Required: false // Required: false
MaxNumPublicIP int64 `url:"maxNumPublicIP,omitempty" json:"maxNumPublicIP,omitempty"` MaxNumPublicIP int64 `url:"maxNumPublicIP,omitempty" json:"maxNumPublicIP,omitempty"`
// If true send emails when a user is granted access to resources // If true send emails when a user is granted access to resources
// Required: false // Required: false
SendAccessEmails bool `url:"sendAccessEmails,omitempty" json:"sendAccessEmails,omitempty"` SendAccessEmails bool `url:"sendAccessEmails" json:"sendAccessEmails"`
// 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"`
// Default zone ID
// Required: false
DefaultZoneID uint64 `url:"defaultZoneId,omitempty" json:"defaultZoneId,omitempty"`
} }
// Update updates an account name and resource types and limits // Update updates an account name and resource types and limits
func (a Account) Update(ctx context.Context, req UpdateRequest) (bool, error) { func (a Account) Update(ctx context.Context, req UpdateRequest) (bool, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return false, validators.ValidationErrors(validators.GetErrors(err))
return false, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/account/update" url := "/cloudapi/account/update"

View File

@@ -8,7 +8,7 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for update user access rights // UpdateUserRequest struct to update user access rights
type UpdateUserRequest struct { type UpdateUserRequest struct {
// ID of the account // ID of the account
// Required: true // Required: true
@@ -30,9 +30,7 @@ type UpdateUserRequest struct {
func (a Account) UpdateUser(ctx context.Context, req UpdateUserRequest) (bool, error) { func (a Account) UpdateUser(ctx context.Context, req UpdateUserRequest) (bool, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return false, validators.ValidationErrors(validators.GetErrors(err))
return false, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/account/updateUser" url := "/cloudapi/account/updateUser"

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,
}
}

View File

@@ -0,0 +1,81 @@
package audit
// FilterByID returns ListAudits with specified ID.
func (la ListAudits) FilterByID(guid string) ListAudits {
predicate := func(ia ItemAudit) bool {
return ia.GUID == guid
}
return la.FilterFunc(predicate)
}
// FilterByCall returns ListAudits with specified call.
func (la ListAudits) FilterByCall(call string) ListAudits {
predicate := func(ic ItemAudit) bool {
return ic.Call == call
}
return la.FilterFunc(predicate)
}
// FilterByCorrelationID returns ListAudits with specified correlation id.
func (la ListAudits) FilterByCorrelationID(correlationID string) ListAudits {
predicate := func(ic ItemAudit) bool {
return ic.CorrelationID == correlationID
}
return la.FilterFunc(predicate)
}
// FilterByRemoteAddr returns ListAudits with specified remote address.
func (la ListAudits) FilterByRemoteAddr(remoteAddr string) ListAudits {
predicate := func(ic ItemAudit) bool {
return ic.RemoteAddr == remoteAddr
}
return la.FilterFunc(predicate)
}
// FilterByUser returns ListAudits with specified user name.
func (la ListAudits) FilterByUser(user string) ListAudits {
predicate := func(ic ItemAudit) bool {
return ic.User == user
}
return la.FilterFunc(predicate)
}
// FilterByStatusCode return ListAudits with specified status code.
func (la ListAudits) FilterByStatusCode(statusCode uint64) ListAudits {
predicate := func(ic ItemAudit) bool {
return ic.StatusCode == statusCode
}
return la.FilterFunc(predicate)
}
// FilterFunc allows filtering ListAudits based on a user-specified predicate.
func (la ListAudits) FilterFunc(predicate func(ItemAudit) bool) ListAudits {
var result ListAudits
for _, item := range la.Data {
if predicate(item) {
result.Data = append(result.Data, item)
}
}
result.EntryCount = uint64(len(result.Data))
return result
}
// FindOne returns first found ItemAudit
// If none was found, returns an empty struct.
func (la ListAudits) FindOne() ItemAudit {
if len(la.Data) == 0 {
return ItemAudit{}
}
return la.Data[0]
}

View File

@@ -0,0 +1,115 @@
package audit
import (
"testing"
)
var audits = ListAudits{
Data: []ItemAudit{
{
Args: "[]",
Call: "/restmachine/cloudapi/audit/linkedJobs",
GUID: "550e8400-e29b-41d4-a716-446655440001",
CorrelationID: "550e8400-e29b-41d4-a716-446655440001",
Kwargs: `{\"audit_guid\":\"dd8623a1-a887-48c1-a500-c10210d404cf\"}`,
RemoteAddr: "192.168.1.100",
ResponseTime: 1,
Result: `[]`,
StatusCode: 200,
Timestamp: 1640995200,
TimestampEnd: 1640995201,
User: "test@example.com",
TTL: "2025-07-31T14:22:57.028000",
},
{
Args: "[]",
Call: "/restmachine/cloudapi/audit/test",
GUID: "550e8400-e29b-41d4-a716-446655440002",
CorrelationID: "550e8400-e29b-41d4-a716-446655440002",
Kwargs: `{\"audit_guid\":\"dd8623a1-a887-48c1-a500-c10210d404cf\"}`,
RemoteAddr: "192.168.1.105",
ResponseTime: 5,
Result: `[]`,
StatusCode: 400,
Timestamp: 1640995200,
TimestampEnd: 1640995201,
User: "test2@example.com",
TTL: "2025-07-31T14:22:57.028000",
},
},
EntryCount: 2,
}
func TestFilterByID(t *testing.T) {
actual := audits.FilterByID("550e8400-e29b-41d4-a716-446655440002").FindOne()
if actual.GUID != "550e8400-e29b-41d4-a716-446655440002" {
t.Fatal("expected GUID 550e8400-e29b-41d4-a716-446655440002, found: ", actual.GUID)
}
actualEmpty := audits.FilterByID("")
if len(actualEmpty.Data) != 0 {
t.Fatal("expected empty, actual: ", len(actualEmpty.Data))
}
}
func TestFilterByCorrelationID(t *testing.T) {
actual := audits.FilterByCorrelationID("550e8400-e29b-41d4-a716-446655440002").FindOne()
if actual.CorrelationID != "550e8400-e29b-41d4-a716-446655440002" {
t.Fatal("expected GUID 550e8400-e29b-41d4-a716-446655440002, found: ", actual.CorrelationID)
}
actualEmpty := audits.FilterByCorrelationID("")
if len(actualEmpty.Data) != 0 {
t.Fatal("expected empty, actual: ", len(actualEmpty.Data))
}
}
func TestFilterByRemoteAddr(t *testing.T) {
actual := audits.FilterByRemoteAddr("192.168.1.100").FindOne()
if actual.RemoteAddr != "192.168.1.100" {
t.Fatal("expected remote address 192.168.1.100, found: ", actual.RemoteAddr)
}
actualEmpty := audits.FilterByRemoteAddr("")
if len(actualEmpty.Data) != 0 {
t.Fatal("expected empty, actual: ", len(actualEmpty.Data))
}
}
func TestFilterByUser(t *testing.T) {
actual := audits.FilterByUser("test@example.com").FindOne()
if actual.User != "test@example.com" {
t.Fatal("expected user test@example.com, found: ", actual.RemoteAddr)
}
actualEmpty := audits.FilterByUser("")
if len(actualEmpty.Data) != 0 {
t.Fatal("expected empty, actual: ", len(actualEmpty.Data))
}
}
func TestFilterByCall(t *testing.T) {
actual := audits.FilterByCall("/restmachine/cloudapi/audit/test").FindOne()
if actual.Call != "/restmachine/cloudapi/audit/test" {
t.Fatal("expected call /restmachine/cloudapi/audit/test, found: ", actual.Call)
}
}
func TestFilterByStatusCode(t *testing.T) {
actual := audits.FilterByStatusCode(200)
for _, item := range actual.Data {
if item.StatusCode != 200 {
t.Fatal("expected 200 status code, found: ", item.StatusCode)
}
}
}

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:"audit_guid" json:"audit_guid" 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
}

124
pkg/cloudapi/audit/list.go Normal file
View File

@@ -0,0 +1,124 @@
package audit
import (
"context"
"encoding/json"
"net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// ListRequest struct to give list of account audits
type ListRequest struct {
// Find all audits after point in time (unixtime)
// Required: false
TimestampAt uint64 `url:"timestamp_at,omitempty" json:"timestamp_at,omitempty"`
// Find all audits before point in time (unixtime)
// Required: false
TimestampTo uint64 `url:"timestamp_to,omitempty" json:"timestamp_to,omitempty"`
// Find by user (Mongo RegExp supported)
// Required: false
User string `url:"user,omitempty" json:"user,omitempty"`
// Find by api endpoint (Mongo RegExp supported)
// Required: false
Call string `url:"call,omitempty" json:"call,omitempty"`
// Find by request id
// Required: false
RequestID string `url:"request_id,omitempty" json:"request_id,omitempty"`
// Find by HTTP min status code
// Required: false
MinStatusCode uint64 `url:"min_status_code,omitempty" json:"min_status_code,omitempty"`
// Find by HTTP max status code
// Required: false
MaxStatusCode uint64 `url:"max_status_code,omitempty" json:"max_status_code,omitempty"`
// Sort by one of supported fields, format +|-(field)
// Required: false
SortBy string `url:"sort_by,omitempty" json:"sort_by,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"`
// Find by resource group id
// Required: false
RGID uint64 `url:"resgroup_id,omitempty" json:"resgroup_id,omitempty"`
// Find by compute id
// Required: false
ComputeID uint64 `url:"compute_id,omitempty" json:"compute_id,omitempty"`
// Find by account id
// Required: false
AccountID uint64 `url:"account_id,omitempty" json:"account_id,omitempty"`
// Find by vins id
// Required: false
VINSID uint64 `url:"vins_id,omitempty" json:"vins_id,omitempty"`
// Find by service id
// Required: false
ServiceID uint64 `url:"service_id,omitempty" json:"service_id,omitempty"`
// Find by k8s id
// Required: false
K8SID uint64 `url:"k8s_id,omitempty" json:"k8s_id,omitempty"`
// Find by flipgroup id
// Required: false
FLIPGroupID uint64 `url:"flipgroup_id,omitempty" json:"flipgroup_id,omitempty"`
// Find by load balancer id
// Required: false
LBID uint64 `url:"lb_id,omitempty" json:"lb_id,omitempty"`
// Find by sep id
// Required: false
SEPID uint64 `url:"sep_id,omitempty" json:"sep_id,omitempty"`
// Exclude audit lines from response
// Required: false
ExcludeAuditLines bool `url:"exclude_audit_lines,omitempty" json:"exclude_audit_lines,omitempty"`
}
// List gets audit records for the specified account object
func (a Audit) List(ctx context.Context, req ListRequest) (*ListAudits, error) {
res, err := a.ListRaw(ctx, req)
if err != nil {
return nil, err
}
list := ListAudits{}
err = json.Unmarshal(res, &list)
if err != nil {
return nil, err
}
return &list, nil
}
// ListRaw gets list of audit records an array of bytes
func (a Audit) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) {
if err := validators.ValidateRequest(req); err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/audit/list"
res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req)
return res, err
}

View File

@@ -0,0 +1,95 @@
package audit
// Main info about audit
type RecordAudit struct {
// Arguments
Arguments string `json:"args"`
// Call
Call string `json:"call"`
// GUID
GUID string `json:"guid"`
// Correlation ID
CorrelationID string `json:"correlation_id"`
// 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"`
}
// Main info about audit
type ItemAudit struct {
// Args
Args string `json:"args"`
// Call
Call string `json:"call"`
// GUID
GUID string `json:"guid"`
// Correlation ID
CorrelationID string `json:"correlation_id"`
// 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"`
// Timestamp
Timestamp float64 `json:"timestamp"`
// Timestamp End
TimestampEnd float64 `json:"timestampEnd"`
// User
User string `json:"user"`
// TTL
TTL string `json:"_ttl"`
}
// List of audits
type ListAudits struct {
// Data
Data []ItemAudit `json:"data"`
// EntryCount
EntryCount uint64 `json:"entryCount"`
}

View File

@@ -8,7 +8,7 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for BasicService // CreateRequest struct for BasicService
type CreateRequest struct { type CreateRequest struct {
// Name of the service // Name of the service
// Required: true // Required: true
@@ -25,15 +25,17 @@ type CreateRequest struct {
// SSH key to deploy for the specified user. Same key will be deployed to all computes of the service // SSH key to deploy for the specified user. Same key will be deployed to all computes of the service
// Required: false // Required: false
SSHKey string `url:"sshKey,omitempty" json:"sshKey,omitempty"` SSHKey string `url:"sshKey,omitempty" json:"sshKey,omitempty"`
// Zone ID
// Required: false
ZoneID uint64 `url:"zoneId,omitempty" json:"zoneId,omitempty"`
} }
// Create creates blank BasicService instance // Create creates blank BasicService instance
func (b BService) Create(ctx context.Context, req CreateRequest) (uint64, error) { func (b BService) Create(ctx context.Context, req CreateRequest) (uint64, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return 0, validators.ValidationErrors(validators.GetErrors(err))
return 0, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/bservice/create" url := "/cloudapi/bservice/create"

View File

@@ -8,14 +8,15 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for delete basic service // DeleteRequest struct to delete basic service
type DeleteRequest struct { type DeleteRequest struct {
// ID of the BasicService to be delete // ID of the BasicService to be delete
// Required: true // Required: true
ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"`
// If set to False, Basic service will be deleted to recycle bin. Otherwise destroyed immediately // If set to False, Basic service will be deleted to recycle bin. Otherwise destroyed immediately
// Required: false // Required: false
// Default: false
Permanently bool `url:"permanently,omitempty" json:"permanently,omitempty"` Permanently bool `url:"permanently,omitempty" json:"permanently,omitempty"`
} }
@@ -23,9 +24,7 @@ type DeleteRequest struct {
func (b BService) Delete(ctx context.Context, req DeleteRequest) (bool, error) { func (b BService) Delete(ctx context.Context, req DeleteRequest) (bool, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return false, validators.ValidationErrors(validators.GetErrors(err))
return false, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/bservice/delete" url := "/cloudapi/bservice/delete"

View File

@@ -8,7 +8,7 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for disable service // DisableRequest struct for disable service
type DisableRequest struct { type DisableRequest struct {
// ID of the service to disable // ID of the service to disable
// Required: true // Required: true
@@ -21,12 +21,10 @@ type DisableRequest struct {
func (b BService) Disable(ctx context.Context, req DisableRequest) (bool, error) { func (b BService) Disable(ctx context.Context, req DisableRequest) (bool, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return false, validators.ValidationErrors(validators.GetErrors(err))
return false, validators.ValidationError(validationError)
}
} }
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

@@ -8,7 +8,7 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for disable service // EnableRequest struct to disable service
type EnableRequest struct { type EnableRequest struct {
// ID of the service to enable // ID of the service to enable
// Required: true // Required: true
@@ -22,9 +22,7 @@ type EnableRequest struct {
func (b BService) Enable(ctx context.Context, req EnableRequest) (bool, error) { func (b BService) Enable(ctx context.Context, req EnableRequest) (bool, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return false, validators.ValidationErrors(validators.GetErrors(err))
return false, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/bservice/enable" url := "/cloudapi/bservice/enable"

View File

@@ -49,21 +49,23 @@ func (lbs ListBasicServices) FilterByTechStatus(techStatus string) ListBasicServ
func (lbs ListBasicServices) FilterFunc(predicate func(ItemBasicService) bool) ListBasicServices { func (lbs ListBasicServices) FilterFunc(predicate func(ItemBasicService) bool) ListBasicServices {
var result ListBasicServices var result ListBasicServices
for _, item := range lbs { for _, item := range lbs.Data {
if predicate(item) { if predicate(item) {
result = append(result, item) result.Data = append(result.Data, item)
} }
} }
result.EntryCount = uint64(len(lbs.Data))
return result return result
} }
// FindOne returns first found ItemBasicService // FindOne returns first found ItemBasicService
// If none was found, returns an empty struct. // If none was found, returns an empty struct.
func (lbs ListBasicServices) FindOne() ItemBasicService { func (lbs ListBasicServices) FindOne() ItemBasicService {
if len(lbs) == 0 { if lbs.EntryCount == 0 {
return ItemBasicService{} return ItemBasicService{}
} }
return lbs[0] return lbs.Data[0]
} }

View File

@@ -3,78 +3,81 @@ package bservice
import "testing" import "testing"
var bservices = ListBasicServices{ var bservices = ListBasicServices{
{ Data: []ItemBasicService{
AccountID: 1, {
AccountName: "std_1", AccountID: 1,
BaseDomain: "", AccountName: "std_1",
CreatedBy: "sample_user_1@decs3o", BaseDomain: "",
CreatedTime: 1677743675, CreatedBy: "sample_user_1@decs3o",
DeletedBy: "", CreatedTime: 1677743675,
DeletedTime: 0, DeletedBy: "",
GID: 212, DeletedTime: 0,
Groups: []uint64{}, GID: 212,
GUID: 1, Groups: []uint64{},
ID: 1, GUID: 1,
Name: "bservice_1", ID: 1,
ParentSrvID: 0, Name: "bservice_1",
ParentSrvType: "", ParentSrvID: 0,
RGID: 7971, ParentSrvType: "",
RGName: "rg_1", RGID: 7971,
SSHUser: "", RGName: "rg_1",
Status: "CREATED", SSHUser: "",
TechStatus: "STOPPED", Status: "CREATED",
UpdatedBy: "", TechStatus: "STOPPED",
UpdatedTime: 0, UpdatedBy: "",
UserManaged: true, UpdatedTime: 0,
}, UserManaged: true,
{ },
AccountID: 2, {
AccountName: "std_2", AccountID: 2,
BaseDomain: "", AccountName: "std_2",
CreatedBy: "sample_user_1@decs3o", BaseDomain: "",
CreatedTime: 1677743736, CreatedBy: "sample_user_1@decs3o",
DeletedBy: "", CreatedTime: 1677743736,
DeletedTime: 0, DeletedBy: "",
GID: 212, DeletedTime: 0,
Groups: []uint64{}, GID: 212,
GUID: 2, Groups: []uint64{},
ID: 2, GUID: 2,
Name: "bservice_2", ID: 2,
ParentSrvID: 0, Name: "bservice_2",
ParentSrvType: "", ParentSrvID: 0,
RGID: 7972, ParentSrvType: "",
RGName: "rg_2", RGID: 7972,
SSHUser: "", RGName: "rg_2",
Status: "CREATED", SSHUser: "",
TechStatus: "STOPPED", Status: "CREATED",
UpdatedBy: "", TechStatus: "STOPPED",
UpdatedTime: 0, UpdatedBy: "",
UserManaged: true, UpdatedTime: 0,
}, UserManaged: true,
{ },
AccountID: 3, {
AccountName: "std_3", AccountID: 3,
BaseDomain: "", AccountName: "std_3",
CreatedBy: "sample_user_2@decs3o", BaseDomain: "",
CreatedTime: 1677743830, CreatedBy: "sample_user_2@decs3o",
DeletedBy: "", CreatedTime: 1677743830,
DeletedTime: 0, DeletedBy: "",
GID: 212, DeletedTime: 0,
Groups: []uint64{}, GID: 212,
GUID: 3, Groups: []uint64{},
ID: 3, GUID: 3,
Name: "bservice_3", ID: 3,
ParentSrvID: 0, Name: "bservice_3",
ParentSrvType: "", ParentSrvID: 0,
RGID: 7973, ParentSrvType: "",
RGName: "rg_3", RGID: 7973,
SSHUser: "", RGName: "rg_3",
Status: "ENABLED", SSHUser: "",
TechStatus: "STARTED", Status: "ENABLED",
UpdatedBy: "", TechStatus: "STARTED",
UpdatedTime: 0, UpdatedBy: "",
UserManaged: true, UpdatedTime: 0,
UserManaged: true,
},
}, },
EntryCount: 3,
} }
func TestFilterByID(t *testing.T) { func TestFilterByID(t *testing.T) {
@@ -104,11 +107,11 @@ func TestFilterByRGID(t *testing.T) {
func TestFilterByStatus(t *testing.T) { func TestFilterByStatus(t *testing.T) {
actual := bservices.FilterByStatus("CREATED") actual := bservices.FilterByStatus("CREATED")
if len(actual) != 2 { if len(actual.Data) != 2 {
t.Fatal("expected 2 found, actual: ", len(actual)) t.Fatal("expected 2 found, actual: ", len(actual.Data))
} }
for _, item := range actual { for _, item := range actual.Data {
if item.Status != "CREATED" { if item.Status != "CREATED" {
t.Fatal("expected Status 'CREATED', found: ", item.Status) t.Fatal("expected Status 'CREATED', found: ", item.Status)
} }
@@ -118,11 +121,11 @@ func TestFilterByStatus(t *testing.T) {
func TestFilterByTechStatus(t *testing.T) { func TestFilterByTechStatus(t *testing.T) {
actual := bservices.FilterByTechStatus("STOPPED") actual := bservices.FilterByTechStatus("STOPPED")
if len(actual) != 2 { if len(actual.Data) != 2 {
t.Fatal("expected 2 found, actual: ", len(actual)) t.Fatal("expected 2 found, actual: ", len(actual.Data))
} }
for _, item := range actual { for _, item := range actual.Data {
if item.TechStatus != "STOPPED" { if item.TechStatus != "STOPPED" {
t.Fatal("expected TechStatus 'STOPPED', found: ", item.TechStatus) t.Fatal("expected TechStatus 'STOPPED', found: ", item.TechStatus)
} }
@@ -134,8 +137,8 @@ func TestFilterFunc(t *testing.T) {
return ibs.CreatedBy == "sample_user_2@decs3o" return ibs.CreatedBy == "sample_user_2@decs3o"
}) })
if len(actual) > 1 { if len(actual.Data) > 1 {
t.Fatal("expected 1 found, actual: ", len(actual)) t.Fatal("expected 1 found, actual: ", len(actual.Data))
} }
if actual.FindOne().CreatedBy != "sample_user_2@decs3o" { if actual.FindOne().CreatedBy != "sample_user_2@decs3o" {
@@ -146,7 +149,7 @@ func TestFilterFunc(t *testing.T) {
func TestSortByCreatedTime(t *testing.T) { func TestSortByCreatedTime(t *testing.T) {
actual := bservices.SortByCreatedTime(true) actual := bservices.SortByCreatedTime(true)
if actual[0].CreatedTime != 1677743830 || actual[2].CreatedTime != 1677743675 { if actual.Data[0].CreatedTime != 1677743830 || actual.Data[2].CreatedTime != 1677743675 {
t.Fatal("expected descending order, found ascending") t.Fatal("expected descending order, found ascending")
} }
} }

View File

@@ -8,35 +8,39 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for get detailed information about service // GetRequest struct to get detailed information about service
type GetRequest struct { type GetRequest struct {
// ID of the service to query information // ID of the service to query information
// Required: true // Required: true
ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"`
} }
// Get gets detailed specifications for the BasicService. // Get gets detailed specifications for the BasicService as a RecordBasicService struct
func (b BService) Get(ctx context.Context, req GetRequest) (*RecordBasicService, error) { func (b BService) Get(ctx context.Context, req GetRequest) (*RecordBasicService, error) {
err := validators.ValidateRequest(req) res, err := b.GetRaw(ctx, req)
if err != nil {
for _, validationError := range validators.GetErrors(err) {
return nil, validators.ValidationError(validationError)
}
}
url := "/cloudapi/bservice/get"
bsRaw, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
info := RecordBasicService{} info := RecordBasicService{}
err = json.Unmarshal(bsRaw, &info) err = json.Unmarshal(res, &info)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &info, nil return &info, nil
} }
// GetRaw gets detailed specifications for the BasicService as an array of bytes
func (b BService) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) {
err := validators.ValidateRequest(req)
if err != nil {
return nil, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/bservice/get"
res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req)
return res, err
}

View File

@@ -8,42 +8,39 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for create new compute group within BasicService // GroupAddRequest struct to create new compute group within BasicService
type GroupAddRequest struct { type GroupAddRequest struct {
// ID of the Basic Service to add a group to // ID of the Basic Service to add a group to
// Required: true // Required: true
ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"`
// Name of the Compute Group to add // Name of the Compute Group to add
// Required: true // Required: true
Name string `url:"name" json:"name" validate:"required"` Name string `url:"name" json:"name" validate:"required"`
// Computes number. Defines how many computes must be there in the group // Computes number. Defines how many computes must be there in the group
// Required: true // Required: true
Count uint64 `url:"count" json:"count" validate:"required"` Count uint64 `url:"count" json:"count" validate:"required"`
// Compute CPU number. All computes in the group have the same CPU count // Compute CPU number. All computes in the group have the same CPU count
// Required: true // Required: true
CPU uint64 `url:"cpu" json:"cpu" validate:"required"` CPU uint64 `url:"cpu" json:"cpu" validate:"required"`
// Compute RAM volume in MB. All computes in the group have the same RAM volume // Compute RAM volume in MB. All computes in the group have the same RAM volume
// Required: true // Required: true
RAM uint64 `url:"ram" json:"ram" validate:"required"` RAM uint64 `url:"ram" json:"ram" validate:"required"`
// Compute boot disk size in GB // Compute boot disk size in GB
// Required: true // Required: true
Disk uint64 `url:"disk" json:"disk" validate:"required"` Disk uint64 `url:"disk" json:"disk" validate:"required"`
// OS image ID to create computes from // OS image ID to create computes from
// Required: true // Required: true
ImageID uint64 `url:"imageId" json:"imageId" validate:"required"` ImageID uint64 `url:"imageId" json:"imageId" validate:"required"`
// Compute driver // Compute driver like a KVM_X86, etc.
// should be one of:
// - KVM_X86
// - KVM_PPC
// Required: true // Required: true
Driver string `url:"driver" json:"driver" validate:"driver"` Driver string `url:"driver" json:"driver" validate:"required"`
// Storage endpoint provider ID // Storage endpoint provider ID
// Required: false // Required: false
@@ -68,6 +65,29 @@ type GroupAddRequest struct {
// Time of Compute Group readiness // Time of Compute Group readiness
// Required: false // Required: false
TimeoutStart uint64 `url:"timeoutStart,omitempty" json:"timeoutStart,omitempty"` TimeoutStart uint64 `url:"timeoutStart,omitempty" json:"timeoutStart,omitempty"`
// Meta data for working group computes, format YAML "user_data": 1111
// Required: false
UserData string `url:"userData,omitempty" json:"userData,omitempty"`
// Chipset "i440fx" or "Q35
// Default value : Q35
// Required: false
Chipset string `url:"chipset,omitempty" json:"chipset,omitempty" validate:"chipset,omitempty"`
// ID of the chosen storage policy
// Required: false
StoragePolicyID uint64 `url:"storage_policy_id,omitempty" json:"storage_policy_id,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.
@@ -76,9 +96,7 @@ type GroupAddRequest struct {
func (b BService) GroupAdd(ctx context.Context, req GroupAddRequest) (uint64, error) { func (b BService) GroupAdd(ctx context.Context, req GroupAddRequest) (uint64, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return 0, validators.ValidationErrors(validators.GetErrors(err))
return 0, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/bservice/groupAdd" url := "/cloudapi/bservice/groupAdd"

View File

@@ -8,11 +8,11 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for remove group compute // GroupComputeRemoveRequest struct to remove group compute
type GroupComputeRemoveRequest struct { type GroupComputeRemoveRequest struct {
// ID of the Basic Service // ID of the Basic Service
// Required: true // Required: true
ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"`
// ID of the Compute GROUP // ID of the Compute GROUP
// Required: true // Required: true
@@ -27,9 +27,7 @@ type GroupComputeRemoveRequest struct {
func (b BService) GroupComputeRemove(ctx context.Context, req GroupComputeRemoveRequest) (bool, error) { func (b BService) GroupComputeRemove(ctx context.Context, req GroupComputeRemoveRequest) (bool, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return false, validators.ValidationErrors(validators.GetErrors(err))
return false, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/bservice/groupComputeRemove" url := "/cloudapi/bservice/groupComputeRemove"

View File

@@ -8,11 +8,11 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for get detailed information about Compute Group // GroupGetRequest struct to get detailed information about Compute Group
type GroupGetRequest struct { type GroupGetRequest struct {
// ID of the Basic Service of Compute Group // ID of the Basic Service of Compute Group
// Required: true // Required: true
ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"`
// ID of the Compute Group // ID of the Compute Group
// Required: true // Required: true
@@ -23,9 +23,7 @@ type GroupGetRequest struct {
func (b BService) GroupGet(ctx context.Context, req GroupGetRequest) (*RecordGroup, error) { func (b BService) GroupGet(ctx context.Context, req GroupGetRequest) (*RecordGroup, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return nil, validators.ValidationErrors(validators.GetErrors(err))
return nil, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/bservice/groupGet" url := "/cloudapi/bservice/groupGet"

View File

@@ -8,12 +8,11 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for add parent Compute Group relation emove parent Compute Group // GroupParentAddRequest struct to add parent Compute Group relation to the specified Compute Group
// relation to the specified Compute Group
type GroupParentAddRequest struct { type GroupParentAddRequest struct {
// ID of the Basic Service of Compute Group // ID of the Basic Service of Compute Group
// Required: true // Required: true
ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"`
// ID of the Compute Group // ID of the Compute Group
// Required: true // Required: true
@@ -28,9 +27,7 @@ type GroupParentAddRequest struct {
func (b BService) GroupParentAdd(ctx context.Context, req GroupParentAddRequest) (bool, error) { func (b BService) GroupParentAdd(ctx context.Context, req GroupParentAddRequest) (bool, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return false, validators.ValidationErrors(validators.GetErrors(err))
return false, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/bservice/groupParentAdd" url := "/cloudapi/bservice/groupParentAdd"

View File

@@ -8,30 +8,28 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for remove parent Compute Group // GroupParentRemoveRequest struct to remove parent Compute Group
// relation to the specified Compute Group // relation from the specified Compute Group
type GroupParentRemoveRequest struct { type GroupParentRemoveRequest struct {
// ID of the Basic Service of Compute Group // ID of the Basic Service of Compute Group
// Required: true // Required: true
ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"`
// ID of the Compute Group // ID of the Compute Group
// Required: true // Required: true
CompGroupID uint64 `url:"compgroupId" json:"compgroupId" validate:"required"` CompGroupID uint64 `url:"compgroupId" json:"compgroupId" validate:"required"`
// ID of the parent Compute Group // ID of the parent Compute Group
// to remove from the current Compute Group // to remove from the current Compute Group
// Required: true // Required: true
ParentID uint64 `url:"parentId" json:"parentId" validate:"required"` ParentID uint64 `url:"parentId" json:"parentId" validate:"required"`
} }
// GroupParentRemove removes parent Compute Group relation to the specified Compute Group // GroupParentRemove removes parent Compute Group relation to the specified Compute Group
func (b BService) GroupParentRemove(ctx context.Context, req GroupParentRemoveRequest) (bool, error) { func (b BService) GroupParentRemove(ctx context.Context, req GroupParentRemoveRequest) (bool, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return false, validators.ValidationErrors(validators.GetErrors(err))
return false, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/bservice/groupParentRemove" url := "/cloudapi/bservice/groupParentRemove"

View File

@@ -8,7 +8,7 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for destroy the specified Compute Group // GroupRemoveRequest struct for destroy the specified Compute Group
type GroupRemoveRequest struct { type GroupRemoveRequest struct {
// ID of the Basic Service of Compute Group // ID of the Basic Service of Compute Group
// Required: true // Required: true
@@ -23,9 +23,7 @@ type GroupRemoveRequest struct {
func (b BService) GroupRemove(ctx context.Context, req GroupRemoveRequest) (bool, error) { func (b BService) GroupRemove(ctx context.Context, req GroupRemoveRequest) (bool, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return false, validators.ValidationErrors(validators.GetErrors(err))
return false, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/bservice/groupRemove" url := "/cloudapi/bservice/groupRemove"

View File

@@ -8,7 +8,7 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for resize the group // GroupResizeRequest struct to resize the group
type GroupResizeRequest struct { type GroupResizeRequest struct {
// ID of the Basic Service of Compute Group // ID of the Basic Service of Compute Group
// Required: true // Required: true
@@ -22,6 +22,12 @@ type GroupResizeRequest struct {
// Required: true // Required: true
Count int64 `url:"count" json:"count" validate:"required"` Count int64 `url:"count" json:"count" validate:"required"`
// Chipset for new computes, either i440fx or Q35 (i440fx by default)
// Available values : i440fx, Q35
// Default value : Q35
// Required: true
Chipset string `url:"chipset" json:"chipset" validate:"required,chipset"`
// Either delta or absolute value of computes // Either delta or absolute value of computes
// Should be one of: // Should be one of:
// - ABSOLUTE // - ABSOLUTE
@@ -34,9 +40,7 @@ type GroupResizeRequest struct {
func (b BService) GroupResize(ctx context.Context, req GroupResizeRequest) (uint64, error) { func (b BService) GroupResize(ctx context.Context, req GroupResizeRequest) (uint64, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return 0, validators.ValidationErrors(validators.GetErrors(err))
return 0, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/bservice/groupResize" url := "/cloudapi/bservice/groupResize"

View File

@@ -8,7 +8,7 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for start the specified Compute Group // GroupStartRequest struct to start the specified Compute Group
type GroupStartRequest struct { type GroupStartRequest struct {
// ID of the Basic Service of Compute Group // ID of the Basic Service of Compute Group
// Required: true // Required: true
@@ -23,9 +23,7 @@ type GroupStartRequest struct {
func (b BService) GroupStart(ctx context.Context, req GroupStartRequest) (bool, error) { func (b BService) GroupStart(ctx context.Context, req GroupStartRequest) (bool, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return false, validators.ValidationErrors(validators.GetErrors(err))
return false, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/bservice/groupStart" url := "/cloudapi/bservice/groupStart"

View File

@@ -8,15 +8,15 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for stop the specified Compute Group // GroupStopRequest struct to stop the specified Compute Group
type GroupStopRequest struct { type GroupStopRequest struct {
// ID of the Basic Service of Compute Group // ID of the Basic Service of Compute Group
// Required: true // Required: true
ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"`
// ID of the Compute Group to stop // ID of the Compute Group to stop
// Required: true // Required: true
CompGroupID uint64 `url:"compgroupId" json:"compgroupId" validate:"required"` CompGroupID uint64 `url:"compgroupId" json:"compgroupId" validate:"required"`
// Force stop Compute Group // Force stop Compute Group
// Required: false // Required: false
@@ -27,9 +27,7 @@ type GroupStopRequest struct {
func (b BService) GroupStop(ctx context.Context, req GroupStopRequest) (bool, error) { func (b BService) GroupStop(ctx context.Context, req GroupStopRequest) (bool, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return false, validators.ValidationErrors(validators.GetErrors(err))
return false, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/bservice/groupStop" url := "/cloudapi/bservice/groupStop"

View File

@@ -8,15 +8,15 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for update existing Compute group // GroupUpdateRequest struct to update existing Compute group
type GroupUpdateRequest struct { type GroupUpdateRequest struct {
// ID of the Basic Service of Compute Group // ID of the Basic Service of Compute Group
// Required: true // Required: true
ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"` ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"`
// ID of the Compute Group // ID of the Compute Group
// Required: true // Required: true
CompGroupID uint64 `url:"compgroupId" json:"compgroupId" validate:"required"` CompGroupID uint64 `url:"compgroupId" json:"compgroupId" validate:"required"`
// Specify non-empty string to update Compute Group name // Specify non-empty string to update Compute Group name
// Required: false // Required: false
@@ -43,13 +43,21 @@ 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)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return false, validators.ValidationErrors(validators.GetErrors(err))
return false, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/bservice/groupUpdate" url := "/cloudapi/bservice/groupUpdate"

View File

@@ -8,7 +8,7 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for update External Network settings // GroupUpdateExtNetRequest struct to update External Network settings
type GroupUpdateExtNetRequest struct { type GroupUpdateExtNetRequest struct {
// ID of the Basic Service of Compute Group // ID of the Basic Service of Compute Group
// Required: true // Required: true
@@ -27,9 +27,7 @@ type GroupUpdateExtNetRequest struct {
func (b BService) GroupUpdateExtNet(ctx context.Context, req GroupUpdateExtNetRequest) (bool, error) { func (b BService) GroupUpdateExtNet(ctx context.Context, req GroupUpdateExtNetRequest) (bool, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return false, validators.ValidationErrors(validators.GetErrors(err))
return false, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/bservice/groupUpdateExtnet" url := "/cloudapi/bservice/groupUpdateExtnet"

View File

@@ -8,7 +8,7 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for update VINS settings // GroupUpdateVINSRequest struct to update VINS settings
type GroupUpdateVINSRequest struct { type GroupUpdateVINSRequest struct {
// ID of the Basic Service of Compute Group // ID of the Basic Service of Compute Group
// Required: true // Required: true
@@ -27,9 +27,7 @@ type GroupUpdateVINSRequest struct {
func (b BService) GroupUpdateVINS(ctx context.Context, req GroupUpdateVINSRequest) (bool, error) { func (b BService) GroupUpdateVINS(ctx context.Context, req GroupUpdateVINSRequest) (bool, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return false, validators.ValidationErrors(validators.GetErrors(err))
return false, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/bservice/groupUpdateVins" url := "/cloudapi/bservice/groupUpdateVins"

View File

@@ -0,0 +1,37 @@
package bservice
// IDs gets array of BasicServiceIDs from ListBasicServices struct
func (lbs ListBasicServices) IDs() []uint64 {
res := make([]uint64, 0, len(lbs.Data))
for _, bs := range lbs.Data {
res = append(res, bs.ID)
}
return res
}
// IDs gets array of ComputeIDs from ListComputes struct
func (lc ListComputes) IDs() []uint64 {
res := make([]uint64, 0, len(lc))
for _, c := range lc {
res = append(res, c.ID)
}
return res
}
// IDs gets array of GroupIDs from ListGroups struct
func (lg ListGroups) IDs() []uint64 {
res := make([]uint64, 0, len(lg))
for _, g := range lg {
res = append(res, g.ID)
}
return res
}
// IDs gets array of GroupComputeIDs from ListGroupComputes struct
func (lgc ListGroupComputes) IDs() []uint64 {
res := make([]uint64, 0, len(lgc))
for _, gc := range lgc {
res = append(res, gc.ID)
}
return res
}

View File

@@ -4,18 +4,53 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"net/http" "net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for get list/deleted list BasicService instances // ListRequest struct to get list of BasicService instances
type ListRequest struct { type ListRequest struct {
// Find by ID
// Required: false
ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"`
// Find by name
// Required: false
Name string `url:"name,omitempty" json:"name,omitempty"`
// ID of the account to query for BasicService instances // ID of the account to query for BasicService instances
// Required: false // Required: false
AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"`
// Find by resource group name
// Required: false
RGName string `url:"rgName,omitempty" json:"rgName,omitempty"`
// ID of the resource group to query for BasicService instances // ID of the resource group to query for BasicService instances
// Required: false // Required: false
RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"` RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"`
// Find by tech status
// Required: false
TechStatus string `url:"techStatus,omitempty" json:"techStatus,omitempty"`
// Find by status
// Required: false
Status string `url:"status,omitempty" json:"status,omitempty"`
// Find by account name
// Required: false
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"`
// Sort by zone id
// Default value: 0
// Required: false
ZoneID uint64 `url:"zone_id,omitempty" json:"zone_id,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"`
@@ -25,40 +60,33 @@ type ListRequest struct {
Size uint64 `url:"size,omitempty" json:"size,omitempty"` Size uint64 `url:"size,omitempty" json:"size,omitempty"`
} }
// List gets list BasicService instances associated with the specified Resource Group // 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)
if err != nil {
return nil, err
}
list := ListBasicServices{}
err = json.Unmarshal(res, &list)
if err != nil {
return nil, err
}
return &list, nil
}
// 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) {
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)
if err != nil { return res, err
return nil, err
}
list := ListBasicServices{}
err = json.Unmarshal(res, &list)
if err != nil {
return nil, err
}
return list, nil
}
// ListDeleted gets list deleted BasicService instances associated with the specified Resource Group
func (b BService) ListDeleted(ctx context.Context, req ListRequest) (ListBasicServices, error) {
url := "/cloudapi/bservice/listDeleted"
res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil {
return nil, err
}
list := ListBasicServices{}
err = json.Unmarshal(res, &list)
if err != nil {
return nil, err
}
return list, nil
} }

View File

@@ -0,0 +1,56 @@
package bservice
import (
"context"
"encoding/json"
"net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// ListDeletedRequest struct to get list of deleted BasicService instances
type ListDeletedRequest struct {
// ID of the account to query for BasicService instances
// Required: false
AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"`
// ID of the resource group to query for BasicService instances
// Required: false
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
// Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"`
// Page size
// Required: false
Size uint64 `url:"size,omitempty" json:"size,omitempty"`
}
// ListDeleted gets list of deleted BasicService instances associated with the specified Resource Group
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"
res, err := b.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil {
return nil, err
}
list := ListBasicServices{}
err = json.Unmarshal(res, &list)
if err != nil {
return nil, err
}
return &list, nil
}

View File

@@ -0,0 +1,42 @@
package bservice
import (
"context"
"net/http"
"strconv"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// MigrateToZone struct to move basic service to another zone
type MigrateToZoneRequest struct {
// ID of the BasicService to move
// Required: true
ServiceID uint64 `url:"serviceId" json:"serviceId" validate:"required"`
// ID of the zone to move
// Required: true
ZoneID uint64 `url:"zoneId" json:"zoneId" validate:"required"`
}
// MigrateToZone moves basic service instance to new zone
func (b BService) MigrateToZone(ctx context.Context, req MigrateToZoneRequest) (bool, error) {
err := validators.ValidateRequest(req)
if err != nil {
return false, validators.ValidationErrors(validators.GetErrors(err))
}
url := "/cloudapi/bservice/migrateToZone"
res, err := b.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

@@ -88,12 +88,15 @@ type RecordBasicService struct {
// Whether user controlled // Whether user controlled
UserManaged bool `json:"userManaged"` UserManaged bool `json:"userManaged"`
// Zone ID
ZoneID uint64 `json:"zoneId"`
} }
// 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"`
@@ -116,8 +119,8 @@ type ItemCompute struct {
// Resource group ID // Resource group ID
RGID uint64 `json:"rgId"` RGID uint64 `json:"rgId"`
// StackID // NodeID
StackID uint64 `json:"stackId"` NodeID uint64 `json:"node_id"`
// Status // Status
Status string `json:"status"` Status string `json:"status"`
@@ -168,9 +171,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
@@ -286,6 +298,9 @@ type ItemGroupCompute struct {
// List of information about OS Users // List of information about OS Users
OSUsers ListOSUsers `json:"osUsers"` OSUsers ListOSUsers `json:"osUsers"`
//Chipset
Chipset string `json:"chipset"`
} }
// List of Group Computes // List of Group Computes
@@ -370,7 +385,14 @@ type ItemBasicService struct {
// User Managed or not // User Managed or not
UserManaged bool `json:"userManaged"` UserManaged bool `json:"userManaged"`
// Zone ID
ZoneID uint64 `json:"zoneId"`
} }
// List of BasicServices // List of BasicServices
type ListBasicServices []ItemBasicService type ListBasicServices struct {
Data []ItemBasicService `json:"data"`
EntryCount uint64 `json:"entryCount"`
}

View File

@@ -8,7 +8,7 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for restores BasicService instance // RestoreRequest struct to restore BasicService instance
type RestoreRequest struct { type RestoreRequest struct {
// ID of the BasicService to be restored // ID of the BasicService to be restored
// Required: true // Required: true
@@ -19,9 +19,7 @@ type RestoreRequest struct {
func (b BService) Restore(ctx context.Context, req RestoreRequest) (bool, error) { func (b BService) Restore(ctx context.Context, req RestoreRequest) (bool, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return false, validators.ValidationErrors(validators.GetErrors(err))
return false, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/bservice/restore" url := "/cloudapi/bservice/restore"

View File

@@ -12,7 +12,7 @@ import (
// - First argument -> prefix // - First argument -> prefix
// - Second argument -> indent // - Second argument -> indent
func (lbs ListBasicServices) Serialize(params ...string) (serialization.Serialized, error) { func (lbs ListBasicServices) Serialize(params ...string) (serialization.Serialized, error) {
if len(lbs) == 0 { if lbs.EntryCount == 0 {
return []byte{}, nil return []byte{}, nil
} }

View File

@@ -8,7 +8,7 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for create snapshot // SnapshotCreateRequest struct to create snapshot
type SnapshotCreateRequest struct { type SnapshotCreateRequest struct {
// ID of the Basic Service // ID of the Basic Service
// Required: true // Required: true
@@ -23,9 +23,7 @@ type SnapshotCreateRequest struct {
func (b BService) SnapshotCreate(ctx context.Context, req SnapshotCreateRequest) (bool, error) { func (b BService) SnapshotCreate(ctx context.Context, req SnapshotCreateRequest) (bool, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return false, validators.ValidationErrors(validators.GetErrors(err))
return false, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/bservice/snapshotCreate" url := "/cloudapi/bservice/snapshotCreate"

View File

@@ -8,7 +8,7 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for delete snapshot // SnapshotDeleteRequest struct to delete snapshot
type SnapshotDeleteRequest struct { type SnapshotDeleteRequest struct {
// ID of the Basic Service // ID of the Basic Service
// Required: true // Required: true
@@ -23,9 +23,7 @@ type SnapshotDeleteRequest struct {
func (b BService) SnapshotDelete(ctx context.Context, req SnapshotDeleteRequest) (bool, error) { func (b BService) SnapshotDelete(ctx context.Context, req SnapshotDeleteRequest) (bool, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return false, validators.ValidationErrors(validators.GetErrors(err))
return false, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/bservice/snapshotDelete" url := "/cloudapi/bservice/snapshotDelete"

View File

@@ -8,7 +8,7 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for get list existing snapshots // SnapshotListRequest struct to get list of existing snapshots
type SnapshotListRequest struct { type SnapshotListRequest struct {
// ID of the Basic Service // ID of the Basic Service
// Required: true // Required: true
@@ -16,12 +16,10 @@ 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 {
for _, validationError := range validators.GetErrors(err) { return nil, validators.ValidationErrors(validators.GetErrors(err))
return nil, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/bservice/snapshotList" url := "/cloudapi/bservice/snapshotList"
@@ -31,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

@@ -8,7 +8,7 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for rollback snapshot // SnapshotRollbackRequest struct to rollback snapshot
type SnapshotRollbackRequest struct { type SnapshotRollbackRequest struct {
// ID of the Basic Service // ID of the Basic Service
// Required: true // Required: true
@@ -23,9 +23,7 @@ type SnapshotRollbackRequest struct {
func (b BService) SnapshotRollback(ctx context.Context, req SnapshotRollbackRequest) (bool, error) { func (b BService) SnapshotRollback(ctx context.Context, req SnapshotRollbackRequest) (bool, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return false, validators.ValidationErrors(validators.GetErrors(err))
return false, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/bservice/snapshotRollback" url := "/cloudapi/bservice/snapshotRollback"

View File

@@ -6,16 +6,16 @@ import "sort"
// //
// If inverse param is set to true, the order is reversed. // If inverse param is set to true, the order is reversed.
func (lbs ListBasicServices) SortByCreatedTime(inverse bool) ListBasicServices { func (lbs ListBasicServices) SortByCreatedTime(inverse bool) ListBasicServices {
if len(lbs) < 2 { if lbs.EntryCount < 2 {
return lbs return lbs
} }
sort.Slice(lbs, func(i, j int) bool { sort.Slice(lbs.Data, func(i, j int) bool {
if inverse { if inverse {
return lbs[i].CreatedTime > lbs[j].CreatedTime return lbs.Data[i].CreatedTime > lbs.Data[j].CreatedTime
} }
return lbs[i].CreatedTime < lbs[j].CreatedTime return lbs.Data[i].CreatedTime < lbs.Data[j].CreatedTime
}) })
return lbs return lbs
@@ -25,16 +25,16 @@ func (lbs ListBasicServices) SortByCreatedTime(inverse bool) ListBasicServices {
// //
// If inverse param is set to true, the order is reversed. // If inverse param is set to true, the order is reversed.
func (lbs ListBasicServices) SortByUpdatedTime(inverse bool) ListBasicServices { func (lbs ListBasicServices) SortByUpdatedTime(inverse bool) ListBasicServices {
if len(lbs) < 2 { if lbs.EntryCount < 2 {
return lbs return lbs
} }
sort.Slice(lbs, func(i, j int) bool { sort.Slice(lbs.Data, func(i, j int) bool {
if inverse { if inverse {
return lbs[i].UpdatedTime > lbs[j].UpdatedTime return lbs.Data[i].UpdatedTime > lbs.Data[j].UpdatedTime
} }
return lbs[i].UpdatedTime < lbs[j].UpdatedTime return lbs.Data[i].UpdatedTime < lbs.Data[j].UpdatedTime
}) })
return lbs return lbs
@@ -44,16 +44,16 @@ func (lbs ListBasicServices) SortByUpdatedTime(inverse bool) ListBasicServices {
// //
// If inverse param is set to true, the order is reversed. // If inverse param is set to true, the order is reversed.
func (lbs ListBasicServices) SortByDeletedTime(inverse bool) ListBasicServices { func (lbs ListBasicServices) SortByDeletedTime(inverse bool) ListBasicServices {
if len(lbs) < 2 { if lbs.EntryCount < 2 {
return lbs return lbs
} }
sort.Slice(lbs, func(i, j int) bool { sort.Slice(lbs.Data, func(i, j int) bool {
if inverse { if inverse {
return lbs[i].DeletedTime > lbs[j].DeletedTime return lbs.Data[i].DeletedTime > lbs.Data[j].DeletedTime
} }
return lbs[i].DeletedTime < lbs[j].DeletedTime return lbs.Data[i].DeletedTime < lbs.Data[j].DeletedTime
}) })
return lbs return lbs

View File

@@ -8,7 +8,7 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for start service // StartRequest struct to start service
type StartRequest struct { type StartRequest struct {
// ID of the service to start // ID of the service to start
// Required: true // Required: true
@@ -21,9 +21,7 @@ type StartRequest struct {
func (b BService) Start(ctx context.Context, req StartRequest) (bool, error) { func (b BService) Start(ctx context.Context, req StartRequest) (bool, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { return false, validators.ValidationErrors(validators.GetErrors(err))
return false, validators.ValidationError(validationError)
}
} }
url := "/cloudapi/bservice/start" url := "/cloudapi/bservice/start"

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