Compare commits

..

37 Commits

Author SHA1 Message Date
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
aaf0857ff0 v1.4.0 2023-04-28 11:46:58 +03:00
7d6cda7119 v1.3.1 2023-04-20 11:17:35 +03:00
469 changed files with 16659 additions and 4954 deletions

6
.gitignore vendored
View File

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

View File

@@ -1,39 +1,10 @@
## Version 1.3.0 ## Version 1.6.6
### Features ## Bugfix
- Fixed model RecordExtNet in cloudbroker/extnet/models for correct work of get request
- Created CloudAPI/CloudBroker filtering, sorting and serialization functions for List requests. - Fixed json tags in ItemResourceConsumption model and delete extra model Consumed in cloudbroker/account/models
- Every handler with present List request has available FilterBy functions. Filtering by ID, Name is common for each handler. - Fixed statelessSepId field type from uint64 to int64 in cloudbroker/compute/models for correct work of list request
- In case user needs to filter response by uncommon field FilterFunc with user-specified predicate is also available.
- CloudAPI/CloudBroker computes, disks and lb also have specific Filter methods predefined, to name a few: ## Feature
- computes: - Added GetRaw and ListRaw methods that give response as an array of bytes for cloudAPI groups: account, compute, k8s, disks, rg, bservice, disks,extnet, flipgroup, image, k8ci, lb, locations(list), sizes(list), stack, tasks, vins
- FilterByK8SID, used to filter computes used by specified k8s cluster; - Added GetRaw and ListRaw methods that give response as an array of bytes for cloudbroker groups: account, apiaccess, compute, disks, extnet, flipgroup, grid, group, image, k8ci, k8s, lb, rg, sep, stack, tasks, user, vgpu, vins
- FilterByK8SMasters, FilterByK8SWorkers, used to filter master/workers nodes. Best used after FilterByK8SID call;
- FilterByLBID, used to filter computes used by specified load balancer;
- disks:
- FilterByK8SID, used to filter disks attached to computes inside specified k8s cluster;
- FilterByLBID, used to filter disks attached to computes inside specified load balancer;
- lb:
- FilterByK8SID, used to filter load balancers used by specified k8s cluster;
- Reinvented request validation using go-validator. Made easier to manipulate and add on to.
- Request/Config validation now uses tags instead of hard-coded validation functions;
- Added ability to parse client configuration from JSON or YAML formatted files.
### Bug Fixes
- Fixed SSO_URL trailing slash possibly breaking authentication process.
- Fixed cloudbroker/vins/nat_rule_add request model types.
- Fixed cloudbroker/grid DiskSize field type
- Fixed TasksResult, InfoResult in cloudbroker/cloudapi/tasks/models JSON unmarshalling.
### Tests
- Covered CloudAPI/CloudBroker filters with unit tests.
### Other
- Updated module to new repository

125
README.md
View File

@@ -8,6 +8,9 @@ Decort SDK - это библиотека, написанная на языке G
- Версия 1.1.x Decort-SDK соответствует 3.8.5 версии платформы - Версия 1.1.x Decort-SDK соответствует 3.8.5 версии платформы
- Версия 1.2.x Decort-SDK соответствует 3.8.5 версии платформы - Версия 1.2.x Decort-SDK соответствует 3.8.5 версии платформы
- Версия 1.3.x Decort-SDK соответствует 3.8.5 версии платформы - Версия 1.3.x Decort-SDK соответствует 3.8.5 версии платформы
- Версия 1.4.x Decort-SDK соответствует 3.8.6 версии платформы
- Версия 1.5.x Decort-SDK соответствует 3.8.7 версии платформы
- Версия 1.6.x Decort-SDK соответствует 3.8.8 версии платформы
## Оглавление ## Оглавление
@@ -60,12 +63,11 @@ go get -u repository.basistech.ru/BASIS/decort-golang-sdk
- `Account` - управление аккаунтами - внутренними учетными записями платформы, которые являются владельцами вычислительных ресурсов; - `Account` - управление аккаунтами - внутренними учетными записями платформы, которые являются владельцами вычислительных ресурсов;
- `BService` - управление группами виртуальных машин (computes); - `BService` - управление группами виртуальных машин (computes);
- `Compute` - управление виртуальными машинами (индивидуально); - `Compute` - управление виртуальными машинами (индивидуально);
- `ComputeCI` - управление конвейром для создания виртуальных машин;
- `Disks` - управление виртуальными дисками; - `Disks` - управление виртуальными дисками;
- `ExtNet` - управление виртуальными сетями, отвечающими за внешний доступ; - `ExtNet` - управление виртуальными сетями, отвечающими за внешний доступ;
- `FLIPgroup` - управление группами "плавающими" ip - адресами; - `FLIPgroup` - управление группами "плавающими" ip - адресами;
- `Image` - управление образами операционных систем; - `Image` - управление образами операционных систем;
- `K8CI` - управление конвейром для создания кластера; - `K8CI` - получение информации о конвейере для создания кластера;
- `K8S` - управление кластерами kubernetes; - `K8S` - управление кластерами kubernetes;
- `KVMPPC` - создание виртуальной машины Power PC (IBM); - `KVMPPC` - создание виртуальной машины Power PC (IBM);
- `KVMx86` - создание виртуальной машины x86; - `KVMx86` - создание виртуальной машины x86;
@@ -73,6 +75,7 @@ go get -u repository.basistech.ru/BASIS/decort-golang-sdk
- `Locations` - получение информации о grid площадки; - `Locations` - получение информации о grid площадки;
- `RG` - управление ресурсными группами аккаунта; - `RG` - управление ресурсными группами аккаунта;
- `Sizes` - получение информации о потребляемых ресурсах виртуальными машинами и дисками; - `Sizes` - получение информации о потребляемых ресурсах виртуальными машинами и дисками;
- `Stack` - получение информации о вычислительных узлах;
- `Tasks` - получение информации о ходе выполнения асинхронных задач (например, создание кластера); - `Tasks` - получение информации о ходе выполнения асинхронных задач (например, создание кластера);
- `VINS` - управление виртуальными изолированными сетями. - `VINS` - управление виртуальными изолированными сетями.
@@ -82,30 +85,39 @@ go get -u repository.basistech.ru/BASIS/decort-golang-sdk
Данная группа ручек позволяет выполнять следующие операции в платформе: Данная группа ручек позволяет выполнять следующие операции в платформе:
- `Account` - управление аккаунтами - внутренними учетными записями платформы, которые являются владельцами вычислительных ресурсов; - `Account` - управление аккаунтами - внутренними учетными записями платформы, которые являются владельцами вычислительных ресурсов;
- `APIAccess` - управление доступом к API и его объектам;
- `Backup` - управление резервным копированием;
- `Compute` - управление виртуальными машинами (индивидуально); - `Compute` - управление виртуальными машинами (индивидуально);
- `Disks` - управление виртуальными дисками; - `Disks` - управление виртуальными дисками;
- `ExtNet` - управление виртуальными сетями, отвечающими за внешний доступ; - `ExtNet` - управление виртуальными сетями, отвечающими за внешний доступ;
- `FLIPGroup` - управление группами с «плавающими» ip адресами;
- `Grid` - управление площадками; - `Grid` - управление площадками;
- `Group` - управление группами пользователей;
- `Image` - управление образами операционных систем; - `Image` - управление образами операционных систем;
- `K8CI` - управление конвейром для создания кластера; - `K8CI` - управление конвейром для создания кластера;
- `K8S` - управление кластерами kubernetes; - `K8S` - управление кластерами kubernetes;
- `KVMPPC` - создание виртуальной машины Power PC (IBM); - `KVMPPC` - создание виртуальной машины Power PC (IBM);
- `KVMx86` - создание виртуальной машины x86; - `KVMx86` - создание виртуальной машины x86;
- `LB` - управление балансировщиками нагрузки; - `LB` - управление балансировщиками нагрузки;
- `PCIDevice` - управление устройствами;
- `RG` - управление ресурсными группами аккаунта; - `RG` - управление ресурсными группами аккаунта;
- `SEP` - управление storage endpoint (sep); - `SEP` - управление storage endpoint (sep);
- `Stack` - получение информации о вычислительных узлах;
- `Tasks` - получение информации о ходе выполнения асинхронных задач (например, создание кластера); - `Tasks` - получение информации о ходе выполнения асинхронных задач (например, создание кластера);
- `User` - управление пользователями (индивидуально);
- `VGPU` - управление виртуальными графическими процессорами;
- `VINS` - управление виртуальными изолированными сетями. - `VINS` - управление виртуальными изолированными сетями.
## Работа с библиотекой ## Работа с библиотекой
Алгоритм работы с библиотекой выглядит следующим образом: Алгоритм работы с библиотекой выглядит следующим образом:
1. Настройка конфигурации клиента. 1. Выполнение одного из действий:
2. Парсинг конфигурации из файла. - настройка конфигурации клиента;
3. Создание клиента. - парсинг конфигурации из файла.
4. Создание структуры запроса. 2. Создание клиента.
5. Выполнение запроса. 3. Создание структуры запроса.
4. Выполнение запроса.
### Настройка конфигурации клиента ### Настройка конфигурации клиента
@@ -117,7 +129,8 @@ go get -u repository.basistech.ru/BASIS/decort-golang-sdk
| SSOURL | string | Да | URL адрес сервиса аутентификации и авторизации | | SSOURL | string | Да | URL адрес сервиса аутентификации и авторизации |
| DecortURL | string | Да | URL адрес платформы, с которой будет осуществляться взаимодействие | | DecortURL | string | Да | URL адрес платформы, с которой будет осуществляться взаимодействие |
| Retries | uint | Нет | Кол-во неудачных попыток выполнения запроса, по умолчанию - 5 | | Retries | uint | Нет | Кол-во неудачных попыток выполнения запроса, по умолчанию - 5 |
| SSLSkipVerify | bool | Нет | Пропуск проверки подлинности сертификата, по умолчанию - true | | Timeout | config.Duration | Нет | Таймаут HTTP клиента, по умолчанию - без ограничений |
| SSLSkipVerify | bool | Нет | Пропуск проверки подлинности сертификата |
| Token | string | Нет | JWT токен | | Token | string | Нет | JWT токен |
#### Пример конфигурации клиента #### Пример конфигурации клиента
@@ -126,6 +139,7 @@ go get -u repository.basistech.ru/BASIS/decort-golang-sdk
import ( import (
"repository.basistech.ru/BASIS/decort-golang-sdk/config" "repository.basistech.ru/BASIS/decort-golang-sdk/config"
) )
func main(){ func main(){
// Настройка конфигурации // Настройка конфигурации
cfg := config.Config{ cfg := config.Config{
@@ -135,6 +149,8 @@ func main(){
DecortURL: "https://mr4.digitalenergy.online", DecortURL: "https://mr4.digitalenergy.online",
Retries: 5, Retries: 5,
} }
cfg.SetTimeout(5 * time.Minute)
} }
``` ```
@@ -164,6 +180,7 @@ func main() {
"ssoUrl": "https://sso.digitalenergy.online", "ssoUrl": "https://sso.digitalenergy.online",
"decortUrl": "https://mr4.digitalenergy.online", "decortUrl": "https://mr4.digitalenergy.online",
"retries": 5, "retries": 5,
"timeout": "5m",
"sslSkipVerify": false "sslSkipVerify": false
} }
``` ```
@@ -176,6 +193,7 @@ appSecret: <APP_SECRET>
ssoUrl: https://sso.digitalenergy.online ssoUrl: https://sso.digitalenergy.online
decortUrl: https://mr4.digitalenergy.online decortUrl: https://mr4.digitalenergy.online
retries: 5 retries: 5
timeout: 5m
sslSkipVerify: false sslSkipVerify: false
``` ```
@@ -203,6 +221,8 @@ func main() {
Retries: 5, Retries: 5,
} }
cfg.SetTimeout(5 * time.Minute)
// Создание клиента // Создание клиента
client := decort.New(cfg) client := decort.New(cfg)
} }
@@ -221,7 +241,6 @@ func main() {
- `pkg/cloudapi/account` - для `Account` - `pkg/cloudapi/account` - для `Account`
- `pkg/cloudapi/bservice` - для `Basic Service` - `pkg/cloudapi/bservice` - для `Basic Service`
- `pkg/cloudapi/compute` - для `Compute` - `pkg/cloudapi/compute` - для `Compute`
- `pkg/cloudapi/computeci` - для `ComputeCI`
- `pkg/cloudapi/disks` - для `Disks` - `pkg/cloudapi/disks` - для `Disks`
- `pkg/cloudapi/extnet` - для `ExtNet` - `pkg/cloudapi/extnet` - для `ExtNet`
- `pkg/cloudapi/flipgroup` - для `FLIPGroup` - `pkg/cloudapi/flipgroup` - для `FLIPGroup`
@@ -233,24 +252,33 @@ func main() {
- `pkg/cloudapi/lb` - для `LB` - `pkg/cloudapi/lb` - для `LB`
- `pkg/cloudapi/locations` - для `Locations` - `pkg/cloudapi/locations` - для `Locations`
- `pkg/cloudapi/rg` - для `RG` - `pkg/cloudapi/rg` - для `RG`
- `pkg/cloudapi/sizes` - для `RG` - `pkg/cloudapi/sizes` - для `Sizes`
- `pkg/cloudapi/stack` - для `Stack`
- `pkg/cloudapi/tasks` - для `Tasks` - `pkg/cloudapi/tasks` - для `Tasks`
- `pkg/cloudapi/vins` - для `VINS` - `pkg/cloudapi/vins` - для `VINS`
- **cloudbroker**: - **cloudbroker**:
- `pkg/cloudbroker/account` - для `Account` - `pkg/cloudbroker/account` - для `Account`
- `pkg/cloudbroker/apiaccess` - для `APIAccess`
- `pkg/cloudbroker/backup` - для `Backup`
- `pkg/cloudbroker/compute` - для `Compute` - `pkg/cloudbroker/compute` - для `Compute`
- `pkg/cloudbroker/disks` - для `Disks` - `pkg/cloudbroker/disks` - для `Disks`
- `pkg/cloudbroker/extnet` - для `ExtNet` - `pkg/cloudbroker/extnet` - для `ExtNet`
- `pkg/cloudbroker/flipgroup` - для `FLIPGroup`
- `pkg/cloudbroker/grid` - для `Grid` - `pkg/cloudbroker/grid` - для `Grid`
- `pkg/cloudbroker/group` - для `Group`
- `pkg/cloudbroker/image` - для `Image` - `pkg/cloudbroker/image` - для `Image`
- `pkg/cloudbroker/k8ci` - для `K8CI` - `pkg/cloudbroker/k8ci` - для `K8CI`
- `pkg/cloudbroker/k8s` - для `K8S` - `pkg/cloudbroker/k8s` - для `K8S`
- `pkg/cloudbroker/kvmppc` - для `KVMPPC` - `pkg/cloudbroker/kvmppc` - для `KVMPPC`
- `pkg/cloudbroker/kvmx86` - для `KVMX86` - `pkg/cloudbroker/kvmx86` - для `KVMX86`
- `pkg/cloudbroker/lb` - для `LB` - `pkg/cloudbroker/lb` - для `LB`
- `pkg/cloudbroker/pcidevice` - для `PCIDevice`
- `pkg/cloudbroker/rg` - для `RG` - `pkg/cloudbroker/rg` - для `RG`
- `pkg/cloudbroker/sep` - для `SEP` - `pkg/cloudbroker/sep` - для `SEP`
- `pkg/cloudbroker/stack` - для `Stack`
- `pkg/cloudbroker/tasks` - для `Tasks` - `pkg/cloudbroker/tasks` - для `Tasks`
- `pkg/cloudbroker/user` - для `User`
- `pkg/cloudbroker/vgpu` - для `VGPU`
- `pkg/cloudbroker/vins` - для `VINS` - `pkg/cloudbroker/vins` - для `VINS`
Все поля структуры имеют описание, в которых содержится: Все поля структуры имеют описание, в которых содержится:
@@ -387,7 +415,7 @@ func main() {
Чтобы выполнить запрос, необходимо: Чтобы выполнить запрос, необходимо:
1. Вызвать у клиента метод, отвечаеющий за определение группы API для взаимодействия, это может быть `.CloudAPI()`, либо `.CloudBroker()`. Данные методы возвращаеют соответствующие структуры, с помощью которых можно соверать запросы. 1. Вызвать у клиента метод, отвечаеющий за определение группы API для взаимодействия, это может быть `.CloudAPI()`, либо `.CloudBroker()`. Данные методы возвращаеют соответствующие структуры, с помощью которых можно совершать запросы.
2. Вызвать у возвращенной структуры метод, определяющий группу ручек для взаимодействия. 2. Вызвать у возвращенной структуры метод, определяющий группу ручек для взаимодействия.
Доступные методы для `.CloudAPI()`: Доступные методы для `.CloudAPI()`:
@@ -395,7 +423,6 @@ func main() {
- `.Account()` - для работы с `Account` - `.Account()` - для работы с `Account`
- `.BService()` - для работы с `BService` - `.BService()` - для работы с `BService`
- `.Compute()` - для работы с `Compute` - `.Compute()` - для работы с `Compute`
- `.ComputeCI()` - для работы с `ComputeCI`
- `.Disks()` - для работы с `Disks` - `.Disks()` - для работы с `Disks`
- `.ExtNet()` - для работы с `ExtNet` - `.ExtNet()` - для работы с `ExtNet`
- `.FLIPgroup()` - для работы с `FLIPGroup` - `.FLIPgroup()` - для работы с `FLIPGroup`
@@ -408,37 +435,46 @@ func main() {
- `.Locations()` - для работы с `Locations` - `.Locations()` - для работы с `Locations`
- `.RG()` - для работы с `RG` - `.RG()` - для работы с `RG`
- `.Sizes()` - для работы с `Sizes` - `.Sizes()` - для работы с `Sizes`
- `.Stack()` - для работы с `Stack`
- `.Tasks()` - для работы с `Tasks` - `.Tasks()` - для работы с `Tasks`
- `.VINS()` - для работы с `VINS` - `.VINS()` - для работы с `VINS`
Доступные методы для `.CloudBroker()`: Доступные методы для `.CloudBroker()`:
- `.Account()` - для работы с `Account` - `.Account()` - для работы с `Account`
- `.APIAccess()` - для работы с `APIAccess`
- `.Backup()` - для работы с `Backup`
- `.Compute()` - для работы с `Compute` - `.Compute()` - для работы с `Compute`
- `.Disks()` - для работы с `Disks` - `.Disks()` - для работы с `Disks`
- `.ExtNet()` - для работы с `ExtNet` - `.ExtNet()` - для работы с `ExtNet`
- `.FLIPGroup()` - для работы с `FLIPGroup`
- `.Grid()` - для работы с `Grid` - `.Grid()` - для работы с `Grid`
- `.Group()` - для работы с `Group`
- `.Image()` - для работы с `Image` - `.Image()` - для работы с `Image`
- `.K8CI()` - для работы с `K8CI` - `.K8CI()` - для работы с `K8CI`
- `.K8S()` - для работы с `K8S` - `.K8S()` - для работы с `K8S`
- `.KVMPPC()` - для работы с `KVMPPC` - `.KVMPPC()` - для работы с `KVMPPC`
- `.KVMx86()` - для работы с `KVMX86` - `.KVMx86()` - для работы с `KVMX86`
- `.LB()` - для работы с `LB` - `.LB()` - для работы с `LB`
- `.PCIDevice()` - для работы с `PCIDevice`
- `.RG()` - для работы с `RG` - `.RG()` - для работы с `RG`
- `.SEP()` - для работы с `SEP` - `.SEP()` - для работы с `SEP`
- `.Stack()` - для работы с `Stack`
- `.Tasks()` - для работы с `Tasks` - `.Tasks()` - для работы с `Tasks`
- `.User()` - для работы с `User`
- `.VGPU()` - для работы с `VGPU`
- `.VINS()` - для работы с `VINS` - `.VINS()` - для работы с `VINS`
3. Вызвать метод, отвечающий за выполнение запроса и передать в него: 3. Вызвать метод, отвечающий за выполнение запроса и передать в него:
- контекст; - контекст;
- структуру запроса. - структуру запроса.
У кождой группы ручек API имеются свои доступные методы, которые определяются платформой. У каждой группы ручек API имеются свои доступные методы, которые определяются платформой.
4. Обработать результат и ошибки. 4. Обработать результат и ошибки.
Т.к. все вызовы методов идут последовательно, можно их объеденить в конвейер: Т.к. все вызовы методов идут последовательно, можно их объеденить в конвейер:
Общий вид вонвейра будет выглядеть так: Общий вид конвейера будет выглядеть так:
```go ```go
client.<API>.<группа>.<метод> client.<API>.<группа>.<метод>
@@ -491,6 +527,56 @@ func main() {
} }
``` ```
Для запросов Get и List реализованы запросы GetRaw и ListRaw, которые возвращают ответ не в виде соответствующей структуры, а в виде массива байт (JSON).
Выполнение таких запросов происходит аналогично.
#### Пример выполнения GetRaw и ListRaw запросов
```go
package main
import (
"log"
"fmt"
"repository.basistech.ru/BASIS/decort-golang-sdk/config"
decort "repository.basistech.ru/BASIS/decort-golang-sdk"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/account"
)
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)
// 1. Создание структуры запроса GetRequest на создание аккаунта и выполнение GetRaw запроса с помощью конвейера
req1 := account.GetRequest{
AccountID: 123,
}
res1, err := client.CloudAPI().Account().GetRaw(context.Background(), req1)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(res1))
// 2. Создание структуры запроса ListRequest на получение аккаунтов и выполнение ListRaw запроса с помощью конвейера
req2 := account.ListRequest{}
res2, err := client.CloudAPI().Account().ListRaw(context.Background(), req2)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(res2))
}
```
### Фильтрация ### Фильтрация
Для каждого `ListRequest` в SDK есть группа функций для фильтрации ответа платформы. Для того чтобы произвести фильтрацию по заданным полям, достаточно описать анонимную функцию (предикат) в `.FilterFunc()`, например: Для каждого `ListRequest` в SDK есть группа функций для фильтрации ответа платформы. Для того чтобы произвести фильтрацию по заданным полям, достаточно описать анонимную функцию (предикат) в `.FilterFunc()`, например:
@@ -646,6 +732,8 @@ func main() {
DecortURL: "<DECORT_URL>", DecortURL: "<DECORT_URL>",
Retries: 5, Retries: 5,
} }
cfg.SetTimeout(5 * time.Minute)
// Создание клиента // Создание клиента
client := decort.New(cfg) client := decort.New(cfg)
@@ -692,7 +780,8 @@ func main() {
| Password | string | Да | пароль legacy пользователя | | Password | string | Да | пароль legacy пользователя |
| DecortURL | string | Да | URL адрес платформы, с которой будет осуществляться взаимодействие | | DecortURL | string | Да | URL адрес платформы, с которой будет осуществляться взаимодействие |
| Retries | uint | Нет | Кол-во неудачных попыток выполнения запроса, по умолчанию - 5 | | Retries | uint | Нет | Кол-во неудачных попыток выполнения запроса, по умолчанию - 5 |
| SSLSkipVerify | bool | Нет | Пропуск проверки подлинности сертификата, по умолчанию - true | | Timeout | config.Duration | Нет | Таймаут HTTP клиента, по умолчанию - без ограничений |
| SSLSkipVerify | bool | Нет | Пропуск проверки подлинности сертификата |
| Token | string | Нет | JWT токен | | Token | string | Нет | JWT токен |
#### Пример конфигурации legacy клиента #### Пример конфигурации legacy клиента
@@ -710,6 +799,8 @@ func main(){
DecortURL: "https://mr4.digitalenergy.online", DecortURL: "https://mr4.digitalenergy.online",
Retries: 5, Retries: 5,
} }
legacyCfg.SetTimeout(5 * time.Minute)
} }
``` ```
@@ -738,6 +829,7 @@ func main() {
"password": "<PASSWORD>", "password": "<PASSWORD>",
"decortUrl": "https://mr4.digitalenergy.online", "decortUrl": "https://mr4.digitalenergy.online",
"retries": 5, "retries": 5,
"timeout": "5m",
"sslSkipVerify": true "sslSkipVerify": true
} }
``` ```
@@ -748,6 +840,7 @@ username: <USERNAME>
password: <PASSWORD> password: <PASSWORD>
decortUrl: https://mr4.digitalenergy.online decortUrl: https://mr4.digitalenergy.online
retries: 5 retries: 5
timeout: 5m
sslSkipVerify: true sslSkipVerify: true
``` ```
### Создание legacy клиента ### Создание legacy клиента
@@ -773,6 +866,8 @@ func main() {
Retries: 5, Retries: 5,
} }
legacyCfg.SetTimeout(5 * time.Minute)
// Создание клиента // Создание клиента
legacyClient := decort.NewLegacy(cfg) legacyClient := decort.NewLegacy(cfg)
} }

407
client.go
View File

@@ -1,24 +1,34 @@
package decortsdk package decortsdk
import ( import (
"bytes"
"context" "context"
"crypto/tls"
"errors" "errors"
"fmt"
"io" "io"
"mime/multipart"
"net/http" "net/http"
"strconv"
"strings" "strings"
"sync"
"time"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi" "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi"
k8s_ca "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/k8s"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker" "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker"
k8s_cb"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/k8s"
"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"
) )
// HTTP-client for platform // 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
@@ -27,9 +37,25 @@ func New(cfg config.Config) *DecortClient {
cfg.Retries = 5 cfg.Retries = 5
} }
var expiryTime time.Time
if cfg.Token != "" {
expiryTime = time.Now().AddDate(0, 0, 1)
}
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: cfg,
expiryTime: expiryTime,
mutex: &sync.Mutex{},
} }
} }
@@ -45,6 +71,283 @@ func (dc *DecortClient) CloudBroker() *cloudbroker.CloudBroker {
// DecortApiCall method for sending requests to the platform // DecortApiCall method for sending requests to the platform
func (dc *DecortClient) DecortApiCall(ctx context.Context, method, url string, params interface{}) ([]byte, error) { func (dc *DecortClient) DecortApiCall(ctx context.Context, method, url string, params interface{}) ([]byte, error) {
k8sCaCreateReq, okCa := params.(k8s_ca.CreateRequest)
k8sCbCreateReq, okCb := params.(k8s_cb.CreateRequest)
if okCa {
reqBody := &bytes.Buffer{}
writer := multipart.NewWriter(reqBody)
if k8sCaCreateReq.OidcCertificate != "" {
part, _ := writer.CreateFormFile("oidcCertificate", "ca.crt")
_, _ = io.Copy(part, strings.NewReader(k8sCaCreateReq.OidcCertificate))
}
_ = writer.WriteField("name", k8sCaCreateReq.Name)
_ = writer.WriteField("rgId", strconv.FormatUint(k8sCaCreateReq.RGID, 10))
_ = writer.WriteField("k8ciId", strconv.FormatUint(k8sCaCreateReq.K8SCIID, 10))
_ = writer.WriteField("workerGroupName", k8sCaCreateReq.WorkerGroupName)
_ = writer.WriteField("networkPlugin", k8sCaCreateReq.NetworkPlugin)
if k8sCaCreateReq.MasterSEPID != 0 {
_ = writer.WriteField("masterSepId", strconv.FormatUint(k8sCaCreateReq.MasterSEPID, 10))
}
if k8sCaCreateReq.MasterSEPPool != "" {
_ = writer.WriteField("masterSepPool", k8sCaCreateReq.MasterSEPPool)
}
if k8sCaCreateReq.WorkerSEPID != 0 {
_ = writer.WriteField("workerSepId", strconv.FormatUint(k8sCaCreateReq.WorkerSEPID, 10))
}
if k8sCaCreateReq.WorkerSEPPool != "" {
_ = writer.WriteField("workerSepPool", k8sCaCreateReq.WorkerSEPPool)
}
if k8sCaCreateReq.Labels != nil {
for _, v := range k8sCaCreateReq.Labels {
_ = writer.WriteField("labels", v)
}
}
if k8sCaCreateReq.Taints != nil {
for _, v := range k8sCaCreateReq.Taints {
_ = writer.WriteField("taints", v)
}
}
if k8sCaCreateReq.Annotations != nil {
for _, v := range k8sCaCreateReq.Annotations {
_ = writer.WriteField("annotations", v)
}
}
if k8sCaCreateReq.MasterCPU != 0 {
_ = writer.WriteField("masterCpu", strconv.FormatUint(uint64(k8sCaCreateReq.MasterCPU), 10))
}
if k8sCaCreateReq.MasterNum != 0 {
_ = writer.WriteField("masterNum", strconv.FormatUint(uint64(k8sCaCreateReq.MasterNum), 10))
}
if k8sCaCreateReq.MasterRAM != 0 {
_ = writer.WriteField("masterRam", strconv.FormatUint(uint64(k8sCaCreateReq.MasterRAM), 10))
}
if k8sCaCreateReq.MasterDisk != 0 {
_ = writer.WriteField("masterDisk", strconv.FormatUint(uint64(k8sCaCreateReq.MasterDisk), 10))
}
if k8sCaCreateReq.WorkerCPU != 0 {
_ = writer.WriteField("workerCpu", strconv.FormatUint(uint64(k8sCaCreateReq.WorkerCPU), 10))
}
if k8sCaCreateReq.WorkerNum != 0 {
_ = writer.WriteField("workerNum", strconv.FormatUint(uint64(k8sCaCreateReq.WorkerNum), 10))
}
if k8sCaCreateReq.WorkerRAM != 0 {
_ = writer.WriteField("workerRam", strconv.FormatUint(uint64(k8sCaCreateReq.WorkerRAM), 10))
}
if k8sCaCreateReq.WorkerDisk != 0 {
_ = writer.WriteField("workerDisk", strconv.FormatUint(uint64(k8sCaCreateReq.WorkerDisk), 10))
}
if k8sCaCreateReq.ExtNetID != 0 {
_ = writer.WriteField("extnetId", strconv.FormatUint(k8sCaCreateReq.ExtNetID, 10))
}
if k8sCaCreateReq.VinsId != 0 {
_ = writer.WriteField("vinsId", strconv.FormatUint(k8sCaCreateReq.VinsId, 10))
}
if !k8sCaCreateReq.WithLB {
_ = writer.WriteField("withLB", strconv.FormatBool(k8sCaCreateReq.WithLB))
}
_ = writer.WriteField("highlyAvailableLB", strconv.FormatBool(k8sCaCreateReq.HighlyAvailable))
if k8sCaCreateReq.AdditionalSANs != nil {
for _, v := range k8sCaCreateReq.AdditionalSANs {
_ = writer.WriteField("additionalSANs", v)
}
}
if k8sCaCreateReq.InitConfiguration != "" {
_ = writer.WriteField("initConfiguration", k8sCaCreateReq.InitConfiguration)
}
if k8sCaCreateReq.ClusterConfiguration != "" {
_ = writer.WriteField("clusterConfiguration", k8sCaCreateReq.ClusterConfiguration)
}
if k8sCaCreateReq.KubeletConfiguration != "" {
_ = writer.WriteField("kubeletConfiguration", k8sCaCreateReq.KubeletConfiguration)
}
if k8sCaCreateReq.KubeProxyConfiguration != "" {
_ = writer.WriteField("kubeProxyConfiguration", k8sCaCreateReq.KubeProxyConfiguration)
}
if k8sCaCreateReq.JoinConfiguration != "" {
_ = writer.WriteField("joinConfiguration", k8sCaCreateReq.JoinConfiguration)
}
if k8sCaCreateReq.Description != "" {
_ = writer.WriteField("desc", k8sCaCreateReq.Description)
}
if k8sCaCreateReq.UserData != "" {
_ = writer.WriteField("userData", k8sCaCreateReq.UserData)
}
_ = writer.WriteField("extnetOnly", strconv.FormatBool(k8sCaCreateReq.ExtNetOnly))
_ = writer.FormDataContentType()
ct := writer.FormDataContentType()
writer.Close()
req, err := http.NewRequestWithContext(ctx, method, dc.decortURL+"/restmachine"+url, reqBody)
if err != nil {
return nil, err
}
if err = dc.getToken(ctx); err != nil {
return nil, err
}
resp, err := dc.domp(req, ct)
if err != nil {
return nil, err
}
defer resp.Body.Close()
respBytes, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
return nil, errors.New(string(respBytes))
}
return respBytes, nil
} else if okCb {
reqBody := &bytes.Buffer{}
writer := multipart.NewWriter(reqBody)
if k8sCbCreateReq.OidcCertificate != "" {
part, _ := writer.CreateFormFile("oidcCertificate", "ca.crt")
_, _ = io.Copy(part, strings.NewReader(k8sCbCreateReq.OidcCertificate))
}
_ = writer.WriteField("name", k8sCbCreateReq.Name)
_ = writer.WriteField("rgId", strconv.FormatUint(k8sCbCreateReq.RGID, 10))
_ = writer.WriteField("k8ciId", strconv.FormatUint(k8sCbCreateReq.K8CIID, 10))
_ = writer.WriteField("workerGroupName", k8sCbCreateReq.WorkerGroupName)
_ = writer.WriteField("networkPlugin", k8sCbCreateReq.NetworkPlugin)
if k8sCbCreateReq.MasterSEPID != 0 {
_ = writer.WriteField("masterSepId", strconv.FormatUint(k8sCbCreateReq.MasterSEPID, 10))
}
if k8sCbCreateReq.MasterSEPPool != "" {
_ = writer.WriteField("masterSepPool", k8sCbCreateReq.MasterSEPPool)
}
if k8sCbCreateReq.WorkerSEPID != 0 {
_ = writer.WriteField("workerSepId", strconv.FormatUint(k8sCbCreateReq.WorkerSEPID, 10))
}
if k8sCbCreateReq.WorkerSEPPool != "" {
_ = writer.WriteField("workerSepPool", k8sCbCreateReq.WorkerSEPPool)
}
if k8sCbCreateReq.Labels != nil {
for _, v := range k8sCbCreateReq.Labels {
_ = writer.WriteField("labels", v)
}
}
if k8sCbCreateReq.Taints != nil {
for _, v := range k8sCbCreateReq.Taints {
_ = writer.WriteField("taints", v)
}
}
if k8sCbCreateReq.Annotations != nil {
for _, v := range k8sCbCreateReq.Annotations {
_ = writer.WriteField("annotations", v)
}
}
if k8sCbCreateReq.MasterCPU != 0 {
_ = writer.WriteField("masterCpu", strconv.FormatUint(k8sCbCreateReq.MasterCPU, 10))
}
if k8sCbCreateReq.MasterNum != 0 {
_ = writer.WriteField("masterNum", strconv.FormatUint(k8sCbCreateReq.MasterNum, 10))
}
if k8sCbCreateReq.MasterRAM != 0 {
_ = writer.WriteField("masterRam", strconv.FormatUint(k8sCbCreateReq.MasterRAM, 10))
}
if k8sCbCreateReq.MasterDisk != 0 {
_ = writer.WriteField("masterDisk", strconv.FormatUint(k8sCbCreateReq.MasterDisk, 10))
}
if k8sCbCreateReq.WorkerCPU != 0 {
_ = writer.WriteField("workerCpu", strconv.FormatUint(k8sCbCreateReq.WorkerCPU, 10))
}
if k8sCbCreateReq.WorkerNum != 0 {
_ = writer.WriteField("workerNum", strconv.FormatUint(k8sCbCreateReq.WorkerNum, 10))
}
if k8sCbCreateReq.WorkerRAM != 0 {
_ = writer.WriteField("workerRam", strconv.FormatUint(k8sCbCreateReq.WorkerRAM, 10))
}
if k8sCbCreateReq.WorkerDisk != 0 {
_ = writer.WriteField("workerDisk", strconv.FormatUint(k8sCbCreateReq.WorkerDisk, 10))
}
if k8sCbCreateReq.ExtNetID != 0 {
_ = writer.WriteField("extnetId", strconv.FormatUint(k8sCbCreateReq.ExtNetID, 10))
}
if k8sCbCreateReq.VinsId != 0 {
_ = writer.WriteField("vinsId", strconv.FormatUint(k8sCbCreateReq.VinsId, 10))
}
if !k8sCbCreateReq.WithLB {
_ = writer.WriteField("withLB", strconv.FormatBool(k8sCbCreateReq.WithLB))
}
_ = writer.WriteField("highlyAvailableLB", strconv.FormatBool(k8sCbCreateReq.HighlyAvailable))
if k8sCbCreateReq.AdditionalSANs != nil {
for _, v := range k8sCbCreateReq.AdditionalSANs {
_ = writer.WriteField("additionalSANs", v)
}
}
if k8sCbCreateReq.InitConfiguration != "" {
_ = writer.WriteField("initConfiguration", k8sCbCreateReq.InitConfiguration)
}
if k8sCbCreateReq.ClusterConfiguration != "" {
_ = writer.WriteField("clusterConfiguration", k8sCbCreateReq.ClusterConfiguration)
}
if k8sCbCreateReq.KubeletConfiguration != "" {
_ = writer.WriteField("kubeletConfiguration", k8sCbCreateReq.KubeletConfiguration)
}
if k8sCbCreateReq.KubeProxyConfiguration != "" {
_ = writer.WriteField("kubeProxyConfiguration", k8sCbCreateReq.KubeProxyConfiguration)
}
if k8sCbCreateReq.JoinConfiguration != "" {
_ = writer.WriteField("joinConfiguration", k8sCbCreateReq.JoinConfiguration)
}
if k8sCbCreateReq.Description != "" {
_ = writer.WriteField("desc", k8sCbCreateReq.Description)
}
if k8sCbCreateReq.UserData != "" {
_ = writer.WriteField("userData", k8sCbCreateReq.UserData)
}
_ = writer.WriteField("extnetOnly", strconv.FormatBool(k8sCbCreateReq.ExtNetOnly))
_ = writer.FormDataContentType()
ct := writer.FormDataContentType()
writer.Close()
req, err := http.NewRequestWithContext(ctx, method, dc.decortURL+"/restmachine"+url, reqBody)
if err != nil {
return nil, err
}
if err = dc.getToken(ctx); err != nil {
return nil, err
}
resp, err := dc.domp(req, ct)
if err != nil {
return nil, err
}
defer resp.Body.Close()
respBytes, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
return nil, errors.New(string(respBytes))
}
return respBytes, nil
}
values, err := query.Values(params) values, err := query.Values(params)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -56,7 +359,11 @@ func (dc *DecortClient) DecortApiCall(ctx context.Context, method, url string, p
return nil, err return nil, err
} }
resp, err := dc.client.Do(req) if err = dc.getToken(ctx); err != nil {
return nil, err
}
resp, err := dc.do(req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -73,3 +380,91 @@ func (dc *DecortClient) DecortApiCall(ctx context.Context, method, url string, p
return respBytes, nil return respBytes, nil
} }
func (dc *DecortClient) getToken(ctx context.Context) error {
dc.mutex.Lock()
defer dc.mutex.Unlock()
if dc.cfg.Token == "" || time.Now().After(dc.expiryTime) {
body := fmt.Sprintf("grant_type=client_credentials&client_id=%s&client_secret=%s&response_type=id_token", dc.cfg.AppID, dc.cfg.AppSecret)
bodyReader := strings.NewReader(body)
dc.cfg.SSOURL = strings.TrimSuffix(dc.cfg.SSOURL, "/")
req, _ := http.NewRequestWithContext(ctx, "POST", dc.cfg.SSOURL+"/v1/oauth/access_token", bodyReader)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
resp, err := dc.client.Do(req)
if err != nil {
return fmt.Errorf("cannot get token: %w", err)
}
tokenBytes, _ := io.ReadAll(resp.Body)
resp.Body.Close()
if resp.StatusCode != 200 {
return fmt.Errorf("cannot get token: %s", tokenBytes)
}
token := string(tokenBytes)
dc.cfg.Token = token
dc.expiryTime = time.Now().AddDate(0, 0, 1)
}
return nil
}
func (dc *DecortClient) do(req *http.Request) (*http.Response, error) {
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Authorization", "bearer "+dc.cfg.Token)
req.Header.Set("Accept", "application/json")
// var resp *http.Response
// var err error
buf, _ := io.ReadAll(req.Body)
// req = req.Clone(req.Context())
// for i := uint64(0); i < dc.cfg.Retries; i++ {
req.Body = io.NopCloser(bytes.NewBuffer(buf))
resp, err := dc.client.Do(req)
// if err == nil {
if resp.StatusCode == 200 {
return resp, err
}
respBytes, _ := io.ReadAll(resp.Body)
err = fmt.Errorf("%s", respBytes)
resp.Body.Close()
// }
// }
return nil, fmt.Errorf("could not execute request: %w", err)
}
func (dc *DecortClient) domp(req *http.Request, ctype string) (*http.Response, error) {
req.Header.Add("Content-Type", ctype)
req.Header.Add("Authorization", "bearer "+dc.cfg.Token)
req.Header.Set("Accept", "application/json")
// var resp *http.Response
// var err error
buf, _ := io.ReadAll(req.Body)
// req = req.Clone(req.Context())
// for i := uint64(0); i < dc.cfg.Retries; i++ {
req.Body = io.NopCloser(bytes.NewBuffer(buf))
resp, err := dc.client.Do(req)
// if err == nil {
if resp.StatusCode == 200 {
return resp, err
}
respBytes, _ := io.ReadAll(resp.Body)
err = fmt.Errorf("%s", respBytes)
resp.Body.Close()
// }
// }
return nil, fmt.Errorf("could not execute request: %w", err)
}

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.

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.

50
config/timeouts.go Normal file
View File

@@ -0,0 +1,50 @@
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
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
default:
return fmt.Errorf("invalid duration %v", value)
}
}
func (d *Duration) Get() time.Duration {
return time.Duration(*d)
}

3
go.mod
View File

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

6
go.sum
View File

@@ -1,3 +1,4 @@
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
@@ -11,10 +12,14 @@ 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-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/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
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/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
@@ -26,6 +31,7 @@ golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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

@@ -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

@@ -1,6 +1,18 @@
package validators package validators
import "github.com/go-playground/validator/v10" import (
"regexp"
"strings"
"github.com/go-playground/validator/v10"
)
// computeDriverValidator is used to validate Driver field in kvmx86/kvmppc create.
func computeDriverValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
return StringInSlice(fieldValue, computeDriverValues)
}
// protoValidator is used to validate Proto fields. // protoValidator is used to validate Proto fields.
func protoValidator(fe validator.FieldLevel) bool { func protoValidator(fe validator.FieldLevel) bool {
@@ -203,3 +215,60 @@ func sepFieldTypeValidator(fe validator.FieldLevel) bool {
return StringInSlice(fieldValue, sepFieldTypeValues) return StringInSlice(fieldValue, sepFieldTypeValues)
} }
// hwPathValidator is used to validate HWPath field.
func hwPathValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
ok, _ := regexp.MatchString(`^\b[0-9a-f]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}.\d{1}$`, fieldValue)
return ok
}
// networkPluginValidator is used to validate NetworkPlugin field
func networkPluginValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
fieldValue = strings.ToLower(fieldValue)
return StringInSlice(fieldValue, networkPluginValues)
}
// networkPluginsValidator is used to validate NetworkPlugins field
func networkPluginsValidator(fe validator.FieldLevel) bool {
fieldSlice, ok := fe.Field().Interface().([]string)
if !ok {
return false
}
for _, item := range fieldSlice {
item = strings.ToLower(item)
if !StringInSlice(item, networkPluginValues) {
return false
}
}
return true
}
func interfaceStateValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
fieldValue = strings.ToLower(fieldValue)
return StringInSlice(fieldValue, interfaceStateValues)
}
func strictLooseValidator(fe validator.FieldLevel) bool {
fieldValue := fe.Field().String()
fieldValue = strings.ToLower(fieldValue)
return StringInSlice(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
}

View File

@@ -107,6 +107,12 @@ func errorMessage(fe validator.FieldError) string {
fe.Field(), fe.Field(),
joinValues(computeDataDisksValues)) joinValues(computeDataDisksValues))
case "computeDriver":
return fmt.Sprintf("%s %s must be one of the following: %s",
prefix,
fe.Field(),
joinValues(computeDriverValues))
// Disk Validators // Disk Validators
case "diskType": case "diskType":
return fmt.Sprintf("%s %s must be one of the following: %s", return fmt.Sprintf("%s %s must be one of the following: %s",
@@ -121,6 +127,12 @@ func errorMessage(fe validator.FieldError) string {
fe.Field(), fe.Field(),
joinValues(flipgroupClientTypeValues)) joinValues(flipgroupClientTypeValues))
// k8s Validators
case "workerGroupName":
return fmt.Sprintf("%s %s must be more 3 symbol",
prefix,
fe.Field())
// KVM_X86/KVM_PPC Validators // KVM_X86/KVM_PPC Validators
case "kvmNetType": case "kvmNetType":
return fmt.Sprintf("%s %s must be one of the following: %s", return fmt.Sprintf("%s %s must be one of the following: %s",
@@ -187,6 +199,36 @@ func errorMessage(fe validator.FieldError) string {
fe.Field(), fe.Field(),
joinValues(sepFieldTypeValues)) joinValues(sepFieldTypeValues))
// HWPath Validators
case "hwPath":
return fmt.Sprintf("%s %s must be in format 0000:1f:2b.0",
prefix,
fe.Field())
// Network plugin Validators
case "networkPlugin":
return fmt.Sprintf("%s %s must be one of the following: %s",
prefix,
fe.Field(),
joinValues(networkPluginValues))
case "networkPlugins":
return fmt.Sprintf("%s %s must contain only the following: %s",
prefix,
fe.Field(),
joinValues(networkPluginValues))
case "strict_loose":
return fmt.Sprintf("%s %s must be one of the following: %s",
prefix,
fe.Field(),
joinValues(strictLooseValues))
case "interfaceState":
return fmt.Sprintf("%s %s must be one of the following: %s",
prefix,
fe.Field(),
joinValues(interfaceStateValues))
} }
return fe.Error() return fe.Error()

View File

@@ -7,29 +7,20 @@ 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.
@@ -39,6 +30,11 @@ func registerAllValidators(validate *validator.Validate) error {
return err return err
} }
err = validate.RegisterValidation("computeDriver", computeDriverValidator)
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
@@ -159,5 +155,35 @@ func registerAllValidators(validate *validator.Validate) error {
return err return err
} }
err = validate.RegisterValidation("hwPath", hwPathValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("networkPlugin", networkPluginValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("networkPlugins", networkPluginsValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("strict_loose", strictLooseValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("interfaceState", interfaceStateValidator)
if err != nil {
return err
}
err = validate.RegisterValidation("workerGroupName", workerGroupNameValidator)
if err != nil {
return err
}
return nil return nil
} }

View File

@@ -6,7 +6,7 @@ var (
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_S", "CU_A", "CU_NO", "CU_I", "CU_NP"}
bserviceModeValues = []string{"ABSOLUTE", "RELATIVE"} bserviceModeValues = []string{"ABSOLUTE", "RELATIVE"}
@@ -17,6 +17,7 @@ var (
computeNetTypeValues = []string{"EXTNET", "VINS"} computeNetTypeValues = []string{"EXTNET", "VINS"}
computeOrderValues = []string{"cdrom", "network", "hd"} computeOrderValues = []string{"cdrom", "network", "hd"}
computeDataDisksValues = []string{"KEEP", "DETACH", "DESTROY"} computeDataDisksValues = []string{"KEEP", "DETACH", "DESTROY"}
computeDriverValues = []string{"KVM_X86", "SVA_KVM_X86"}
diskTypeValues = []string{"B", "T", "D"} diskTypeValues = []string{"B", "T", "D"}
@@ -37,4 +38,10 @@ var (
imageArchitectureValues = []string{"X86_64", "PPC64_LE"} 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"}
strictLooseValues = []string{"strict", "loose"}
interfaceStateValues = []string{"on", "off"}
) )

View File

@@ -1,23 +1,34 @@
package decortsdk package decortsdk
import ( import (
"bytes"
"context" "context"
"crypto/tls"
"errors" "errors"
"fmt"
"io" "io"
"mime/multipart"
"net/http" "net/http"
"net/url"
"strconv"
"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/pkg/cloudapi" "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/k8s"
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker" "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker"
) )
// Legacy HTTP-client for platform // 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 +37,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: cfg,
expiryTime: expiryTime,
mutex: &sync.Mutex{},
} }
} }
@@ -44,18 +71,165 @@ func (ldc *LegacyDecortClient) CloudBroker() *cloudbroker.CloudBroker {
// DecortApiCall method for sending requests to the platform // DecortApiCall method for sending requests to the platform
func (ldc *LegacyDecortClient) DecortApiCall(ctx context.Context, method, url string, params interface{}) ([]byte, error) { func (ldc *LegacyDecortClient) DecortApiCall(ctx context.Context, method, url string, params interface{}) ([]byte, error) {
if k8sCreateReq, ok := params.(k8s.CreateRequest); ok {
reqBody := &bytes.Buffer{}
writer := multipart.NewWriter(reqBody)
if k8sCreateReq.OidcCertificate != "" {
part, _ := writer.CreateFormFile("oidcCertificate", "ca.crt")
_, _ = io.Copy(part, strings.NewReader(k8sCreateReq.OidcCertificate))
}
_ = writer.WriteField("name", k8sCreateReq.Name)
_ = writer.WriteField("rgId", strconv.FormatUint(k8sCreateReq.RGID, 10))
_ = writer.WriteField("k8ciId", strconv.FormatUint(k8sCreateReq.K8SCIID, 10))
_ = writer.WriteField("workerGroupName", k8sCreateReq.WorkerGroupName)
_ = writer.WriteField("networkPlugin", k8sCreateReq.NetworkPlugin)
if k8sCreateReq.MasterSEPID != 0 {
_ = writer.WriteField("masterSepId", strconv.FormatUint(k8sCreateReq.MasterSEPID, 10))
}
if k8sCreateReq.MasterSEPPool != "" {
_ = writer.WriteField("masterSepPool", k8sCreateReq.MasterSEPPool)
}
if k8sCreateReq.WorkerSEPID != 0 {
_ = writer.WriteField("workerSepId", strconv.FormatUint(k8sCreateReq.WorkerSEPID, 10))
}
if k8sCreateReq.WorkerSEPPool != "" {
_ = writer.WriteField("workerSepPool", k8sCreateReq.WorkerSEPPool)
}
if k8sCreateReq.Labels != nil {
for _, v := range k8sCreateReq.Labels {
_ = writer.WriteField("labels", v)
}
}
if k8sCreateReq.Taints != nil {
for _, v := range k8sCreateReq.Taints {
_ = writer.WriteField("taints", v)
}
}
if k8sCreateReq.Annotations != nil {
for _, v := range k8sCreateReq.Annotations {
_ = writer.WriteField("annotations", v)
}
}
if k8sCreateReq.MasterCPU != 0 {
_ = writer.WriteField("masterCpu", strconv.FormatUint(uint64(k8sCreateReq.MasterCPU), 10))
}
if k8sCreateReq.MasterNum != 0 {
_ = writer.WriteField("masterNum", strconv.FormatUint(uint64(k8sCreateReq.MasterNum), 10))
}
if k8sCreateReq.MasterRAM != 0 {
_ = writer.WriteField("masterRam", strconv.FormatUint(uint64(k8sCreateReq.MasterRAM), 10))
}
if k8sCreateReq.MasterDisk != 0 {
_ = writer.WriteField("masterDisk", strconv.FormatUint(uint64(k8sCreateReq.MasterDisk), 10))
}
if k8sCreateReq.WorkerCPU != 0 {
_ = writer.WriteField("workerCpu", strconv.FormatUint(uint64(k8sCreateReq.WorkerCPU), 10))
}
if k8sCreateReq.WorkerNum != 0 {
_ = writer.WriteField("workerNum", strconv.FormatUint(uint64(k8sCreateReq.WorkerNum), 10))
}
if k8sCreateReq.WorkerRAM != 0 {
_ = writer.WriteField("workerRam", strconv.FormatUint(uint64(k8sCreateReq.WorkerRAM), 10))
}
if k8sCreateReq.WorkerDisk != 0 {
_ = writer.WriteField("workerDisk", strconv.FormatUint(uint64(k8sCreateReq.WorkerDisk), 10))
}
if k8sCreateReq.ExtNetID != 0 {
_ = writer.WriteField("extnetId", strconv.FormatUint(k8sCreateReq.ExtNetID, 10))
}
if k8sCreateReq.VinsId != 0 {
_ = writer.WriteField("vinsId", strconv.FormatUint(k8sCreateReq.VinsId, 10))
}
if !k8sCreateReq.WithLB {
_ = writer.WriteField("withLB", strconv.FormatBool(k8sCreateReq.WithLB))
}
_ = writer.WriteField("highlyAvailableLB", strconv.FormatBool(k8sCreateReq.HighlyAvailable))
if k8sCreateReq.AdditionalSANs != nil {
for _, v := range k8sCreateReq.AdditionalSANs {
_ = writer.WriteField("additionalSANs", v)
}
}
if k8sCreateReq.InitConfiguration != "" {
_ = writer.WriteField("initConfiguration", k8sCreateReq.InitConfiguration)
}
if k8sCreateReq.ClusterConfiguration != "" {
_ = writer.WriteField("clusterConfiguration", k8sCreateReq.ClusterConfiguration)
}
if k8sCreateReq.KubeletConfiguration != "" {
_ = writer.WriteField("kubeletConfiguration", k8sCreateReq.KubeletConfiguration)
}
if k8sCreateReq.KubeProxyConfiguration != "" {
_ = writer.WriteField("kubeProxyConfiguration", k8sCreateReq.KubeProxyConfiguration)
}
if k8sCreateReq.JoinConfiguration != "" {
_ = writer.WriteField("joinConfiguration", k8sCreateReq.JoinConfiguration)
}
if k8sCreateReq.Description != "" {
_ = writer.WriteField("desc", k8sCreateReq.Description)
}
if k8sCreateReq.UserData != "" {
_ = writer.WriteField("userData", k8sCreateReq.UserData)
}
_ = writer.WriteField("extnetOnly", strconv.FormatBool(k8sCreateReq.ExtNetOnly))
_ = writer.FormDataContentType()
ct := writer.FormDataContentType()
if err := ldc.getToken(ctx); err != nil {
return nil, err
}
_ = writer.WriteField("authkey", ldc.cfg.Token)
writer.Close()
req, err := http.NewRequestWithContext(ctx, method, ldc.decortURL+"/restmachine"+url, reqBody)
if err != nil {
return nil, err
}
resp, err := ldc.domp(req, ct)
if err != nil {
return nil, err
}
defer resp.Body.Close()
respBytes, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode != 200 {
return nil, errors.New(string(respBytes))
}
return respBytes, nil
}
values, err := query.Values(params) values, err := query.Values(params)
if err != nil { if err != nil {
return nil, err return nil, err
} }
body := strings.NewReader(values.Encode()) if err = ldc.getToken(ctx); err != nil {
return nil, err
}
body := strings.NewReader(values.Encode() + fmt.Sprintf("&authkey=%s", ldc.cfg.Token))
req, err := http.NewRequestWithContext(ctx, method, ldc.decortURL+"/restmachine"+url, body) req, err := http.NewRequestWithContext(ctx, method, ldc.decortURL+"/restmachine"+url, body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
resp, err := ldc.client.Do(req) resp, err := ldc.do(req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -72,3 +246,88 @@ func (ldc *LegacyDecortClient) DecortApiCall(ctx context.Context, method, url st
return respBytes, nil return respBytes, nil
} }
func (ldc *LegacyDecortClient) getToken(ctx context.Context) error {
ldc.mutex.Lock()
defer ldc.mutex.Unlock()
if ldc.cfg.Token == "" || time.Now().After(ldc.expiryTime) {
body := fmt.Sprintf("username=%s&password=%s", url.QueryEscape(ldc.cfg.Username), url.QueryEscape(ldc.cfg.Password))
bodyReader := strings.NewReader(body)
req, _ := http.NewRequestWithContext(ctx, "POST", ldc.cfg.DecortURL+"/restmachine/cloudapi/user/authenticate", bodyReader)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
resp, err := ldc.client.Do(req)
if err != nil {
return fmt.Errorf("unable to get token: %w", err)
}
tokenBytes, _ := io.ReadAll(resp.Body)
resp.Body.Close()
if resp.StatusCode != 200 {
return fmt.Errorf("unable to get token: %s", tokenBytes)
}
token := string(tokenBytes)
ldc.cfg.Token = token
ldc.expiryTime = time.Now().AddDate(0, 0, 1)
}
return nil
}
func (ldc *LegacyDecortClient) do(req *http.Request) (*http.Response, error) {
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Accept", "application/json")
// var resp *http.Response
// var err error
buf, _ := io.ReadAll(req.Body)
// req = req.Clone(req.Context())
// for i := uint64(0); i < ldc.cfg.Retries; i++ {
req.Body = io.NopCloser(bytes.NewBuffer(buf))
resp, err := ldc.client.Do(req)
// if err == nil {
if resp.StatusCode == 200 {
return resp, err
}
respBytes, _ := io.ReadAll(resp.Body)
err = fmt.Errorf("%s", respBytes)
resp.Body.Close()
// }
// }
return nil, fmt.Errorf("could not execute request: %w", err)
}
func (ldc *LegacyDecortClient) domp(req *http.Request, ctype string) (*http.Response, error) {
req.Header.Add("Content-Type", ctype)
req.Header.Add("Authorization", "bearer "+ldc.cfg.Token)
req.Header.Set("Accept", "application/json")
// var resp *http.Response
// var err error
buf, _ := io.ReadAll(req.Body)
// req = req.Clone(req.Context())
// for i := uint64(0); i < ldc.cfg.Retries; i++ {
req.Body = io.NopCloser(bytes.NewBuffer(buf))
resp, err := ldc.client.Do(req)
// if err == nil {
if resp.StatusCode == 200 {
return resp, err
}
respBytes, _ := io.ReadAll(resp.Body)
err = fmt.Errorf("%s", respBytes)
resp.Body.Close()
// }
// }
return nil, fmt.Errorf("could not execute request: %w", err)
}

View File

@@ -44,7 +44,7 @@ type CreateRequest struct {
// 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

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: []RecordACL{
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: []RecordACL{
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: []RecordACL{
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,18 @@ 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 {
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)
return res, err
}

View File

@@ -20,6 +20,7 @@ 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)

View File

@@ -26,6 +26,7 @@ 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_DM: returns consumed max virtual disk storage in GB
// - CU_S: returns consumed primary storage (NAS) in TB // - CU_S: returns consumed primary storage (NAS) in TB
// - CU_A: returns consumed secondary storage (Archive) in TB // - CU_A: returns consumed secondary storage (Archive) in TB
// - CU_NO: returns sent/received network transfer in operator in GB // - CU_NO: returns sent/received network transfer in operator in GB

View File

@@ -21,6 +21,7 @@ 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)

View File

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

@@ -6,22 +6,36 @@ import (
"net/http" "net/http"
) )
// 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"`
// 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.ListRaw(ctx, req)
res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -33,5 +47,13 @@ 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) {
url := "/cloudapi/account/list"
res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req)
return res, err
} }

View File

@@ -13,10 +13,50 @@ 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"`
// 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) { for _, validationError := range validators.GetErrors(err) {
@@ -38,5 +78,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

@@ -10,15 +10,27 @@ import (
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"`
} }
// ListDeleted gets list all deleted accounts the user has access to // ListDeleted gets list 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) {
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 +45,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

@@ -13,10 +13,34 @@ 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"`
// 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) { for _, validationError := range validators.GetErrors(err) {
@@ -38,5 +62,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

@@ -10,13 +10,45 @@ import (
// Request struct for get list FLIPGroups // Request struct for get list 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"`
// 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) { for _, validationError := range validators.GetErrors(err) {
@@ -38,5 +70,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

@@ -13,10 +13,38 @@ 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"`
} }
// ListRG gets list all resource groups under specified account, accessible by the user // ListRG gets list 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) { for _, validationError := range validators.GetErrors(err) {
@@ -38,5 +66,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

@@ -12,15 +12,35 @@ import (
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"`
// 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 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) { for _, validationError := range validators.GetErrors(err) {
@@ -42,5 +62,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

@@ -13,10 +13,34 @@ 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"`
// 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 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) { for _, validationError := range validators.GetErrors(err) {
@@ -38,5 +62,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

@@ -32,6 +32,9 @@ 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"`
@@ -70,7 +73,11 @@ type ItemAccount struct {
} }
// List of accounts // List of accounts
type ListAccounts []ItemAccount type ListAccounts struct {
Data []ItemAccount `json:"data"`
EntryCount uint64 `json:"entryCount"`
}
// Resources used // Resources used
type Resource struct { type Resource struct {
@@ -81,7 +88,7 @@ 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"`
@@ -105,16 +112,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
@@ -140,9 +164,6 @@ type RecordAccount struct {
// DCLocation // DCLocation
DCLocation string `json:"DCLocation"` DCLocation string `json:"DCLocation"`
// Resources
Resources Resources `json:"Resources"`
// CKey // CKey
CKey string `json:"_ckey"` CKey string `json:"_ckey"`
@@ -158,6 +179,12 @@ type RecordAccount struct {
// Computes // Computes
Computes Computes `json:"computes"` Computes Computes `json:"computes"`
// CPU allocation parameter
CPUAllocationParameter string `json:"cpu_allocation_parameter"`
// CPU allocation ratio
CPUAllocationRatio float64 `json:"cpu_allocation_ratio"`
// Created by // Created by
CreatedBy string `json:"createdBy"` CreatedBy string `json:"createdBy"`
@@ -165,7 +192,7 @@ type RecordAccount struct {
CreatedTime uint64 `json:"createdTime"` CreatedTime uint64 `json:"createdTime"`
// Deactivation time // Deactivation time
DeactivationTime uint64 `json:"deactivationTime"` DeactivationTime float64 `json:"deactivationTime"`
// Deleted by // Deleted by
DeletedBy string `json:"deletedBy"` DeletedBy string `json:"deletedBy"`
@@ -192,7 +219,7 @@ type RecordAccount struct {
ResourceLimits ResourceLimits `json:"resourceLimits"` ResourceLimits ResourceLimits `json:"resourceLimits"`
// Resource types // Resource types
ResourceTypes []string `json:"resourceTypes"` ResTypes []string `json:"resourceTypes"`
// Send access emails // Send access emails
SendAccessEmails bool `json:"sendAccessEmails"` SendAccessEmails bool `json:"sendAccessEmails"`
@@ -280,7 +307,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 {
@@ -307,7 +340,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 {
@@ -364,7 +403,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 {
@@ -478,7 +523,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 {
@@ -514,7 +565,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 {
@@ -583,4 +640,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,8 +3,6 @@ package account
import ( import (
"context" "context"
"net/http" "net/http"
"strconv"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
@@ -26,15 +24,10 @@ func (a Account) Restore(ctx context.Context, req RestoreRequest) (bool, error)
url := "/cloudapi/account/restore" url := "/cloudapi/account/restore"
res, err := a.client.DecortApiCall(ctx, http.MethodPost, url, req) _, err = a.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil { if err != nil {
return false, err return false, err
} }
result, err := strconv.ParseBool(string(res)) return true, nil
if err != nil {
return false, err
}
return 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

@@ -20,31 +20,31 @@ type UpdateRequest struct {
// Max size of memory in MB // Max size of memory in MB
// Required: false // Required: false
MaxMemoryCapacity uint64 `url:"maxMemoryCapacity,omitempty" json:"maxMemoryCapacity,omitempty"` MaxMemoryCapacity int64 `url:"maxMemoryCapacity,omitempty" json:"maxMemoryCapacity,omitempty"`
// Max size of aggregated vdisks in GB // Max size of aggregated vdisks in GB
// Required: false // Required: false
MaxVDiskCapacity uint64 `url:"maxVDiskCapacity,omitempty" json:"maxVDiskCapacity,omitempty"` MaxVDiskCapacity int64 `url:"maxVDiskCapacity,omitempty" json:"maxVDiskCapacity,omitempty"`
// Max number of CPU cores // Max number of CPU cores
// Required: false // Required: false
MaxCPUCapacity uint64 `url:"maxCPUCapacity,omitempty" json:"maxCPUCapacity,omitempty"` MaxCPUCapacity int64 `url:"maxCPUCapacity,omitempty" json:"maxCPUCapacity,omitempty"`
// Max sent/received network transfer peering // Max sent/received network transfer peering
// Required: false // Required: false
MaxNetworkPeerTransfer uint64 `url:"maxNetworkPeerTransfer,omitempty" json:"maxNetworkPeerTransfer,omitempty"` 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 uint64 `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 uint64 `url:"gpu_units,omitempty" json:"gpu_units,omitempty"` GPUUnits int64 `url:"gpu_units,omitempty" json:"gpu_units,omitempty"`
} }
// Update updates an account name and resource types and limits // Update updates an account name and resource types and limits

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,15 +8,32 @@ 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) {
res, err := b.GetRaw(ctx, req)
if err != nil {
return nil, err
}
info := RecordBasicService{}
err = json.Unmarshal(res, &info)
if err != nil {
return nil, err
}
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) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { for _, validationError := range validators.GetErrors(err) {
@@ -26,17 +43,6 @@ func (b BService) Get(ctx context.Context, req GetRequest) (*RecordBasicService,
url := "/cloudapi/bservice/get" url := "/cloudapi/bservice/get"
bsRaw, 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
}
info := RecordBasicService{}
err = json.Unmarshal(bsRaw, &info)
if err != nil {
return nil, err
}
return &info, nil
} }

View File

@@ -68,6 +68,10 @@ 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"`
} }
// GroupAdd creates new Compute Group within BasicService. // GroupAdd creates new Compute Group within BasicService.

View File

@@ -6,16 +6,40 @@ import (
"net/http" "net/http"
) )
// 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"`
// 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 +49,27 @@ 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) {
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,45 @@
package bservice
import (
"context"
"encoding/json"
"net/http"
)
// Request struct for 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"`
// 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 deleted BasicService instances associated with the specified Resource Group
func (b BService) ListDeleted(ctx context.Context, req ListDeletedRequest) (*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

@@ -35,11 +35,8 @@ type RecordBasicService struct {
// Grid ID // Grid ID
GID uint64 `json:"gid"` GID uint64 `json:"gid"`
// List of Service Compute Group IDs // List of Service Compute Groups
Groups []uint64 `json:"groups"` Groups ListGroups `json:"groups"`
// List of compute groups by name
GroupsName []string `json:"groupsName"`
// GUID // GUID
GUID uint64 `json:"guid"` GUID uint64 `json:"guid"`
@@ -95,6 +92,12 @@ type RecordBasicService struct {
// Main information about Compute // Main information about Compute
type ItemCompute struct { type ItemCompute struct {
// Account ID
AccountID uint64
// Architecture
Architecture string `json:"arch"`
// Compute group ID // Compute group ID
CompGroupID uint64 `json:"compgroupId"` CompGroupID uint64 `json:"compgroupId"`
@@ -109,11 +112,47 @@ type ItemCompute struct {
// Name // Name
Name string `json:"name"` Name string `json:"name"`
// Resource group ID
RGID uint64 `json:"rgId"`
// StackID
StackID uint64 `json:"stackId"`
// Status
Status string `json:"status"`
// Tech status
TechStatus string `json:"techStatus"`
} }
// List of Computes // List of Computes
type ListComputes []ItemCompute type ListComputes []ItemCompute
// Main information about Group
type ItemGroup struct {
// Amount of computes
Computes uint64 `json:"computes"`
// Consistency
Consistency bool `json:"consistency"`
// Group ID
ID uint64 `json:"id"`
// Group name
Name string `json:"name"`
// Status
Status string `json:"status"`
// TechStatus
TechStatus string `json:"techStatus"`
}
// List of Groups
type ListGroups []ItemGroup
// Main information about Snapshot // Main information about Snapshot
type ItemSnapshot struct { type ItemSnapshot struct {
// GUID // GUID
@@ -334,4 +373,8 @@ type ItemBasicService struct {
} }
// List of BasicServices // List of BasicServices
type ListBasicServices []ItemBasicService type ListBasicServices struct {
Data []ItemBasicService `json:"data"`
EntryCount uint64 `json:"entryCount"`
}

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

@@ -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

@@ -0,0 +1,41 @@
package compute
import (
"context"
"encoding/json"
"net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// Request struct for getting boot order
type BootOrderGetRequest struct {
// Compute ID
// Required: true
ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"`
}
// BootOrderGet gets actual compute boot order information
func (c Compute) BootOrderGet(ctx context.Context, req BootOrderGetRequest) ([]string, error) {
err := validators.ValidateRequest(req)
if err != nil {
for _, validationError := range validators.GetErrors(err) {
return nil, validators.ValidationError(validationError)
}
}
url := "/cloudapi/compute/bootOrderGet"
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil {
return nil, err
}
orders := make([]string, 0)
err = json.Unmarshal(res, &orders)
if err != nil {
return nil, err
}
return orders, nil
}

View File

@@ -0,0 +1,49 @@
package compute
import (
"context"
"encoding/json"
"net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// Request struct for setting boot order
type BootOrderSetRequest struct {
// ID of compute instance
// Required: true
ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"`
// List of boot devices
// Should be one of:
// - cdrom
// - network
// - hd
// Required: true
Order []string `url:"order" json:"order" validate:"min=1,computeOrder"`
}
// BootOrderSet sets compute boot order
func (c Compute) BootOrderSet(ctx context.Context, req BootOrderSetRequest) ([]string, error) {
err := validators.ValidateRequest(req)
if err != nil {
for _, validationError := range validators.GetErrors(err) {
return nil, validators.ValidationError(validationError)
}
}
url := "/cloudapi/compute/bootOrderSet"
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil {
return nil, err
}
orders := make([]string, 0)
err = json.Unmarshal(res, &orders)
if err != nil {
return nil, err
}
return orders, nil
}

View File

@@ -0,0 +1,47 @@
package compute
import (
"context"
"net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
"strconv"
)
// Request struct for changing link state
type ChangeLinkStateRequest struct {
// Compute ID
// Required: true
ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"`
// Interface name or MAC address
// Required: true
Interface string `url:"interface" json:"interface" validate:"required"`
// Interface state
// Must be either "on" or "off"
// Required: true
State string `url:"state" json:"state" validate:"required,interfaceState"`
}
// ChangeLinkState changes the status link virtual of compute
func (c Compute) ChangeLinkState(ctx context.Context, req ChangeLinkStateRequest) (bool, error) {
err := validators.ValidateRequest(req)
if err != nil {
for _, validationError := range validators.GetErrors(err) {
return false, validators.ValidationError(validationError)
}
}
url := "/cloudapi/compute/changeLinkState"
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil {
return false, err
}
result, err := strconv.ParseBool(string(res))
if err != nil {
return false, err
}
return result, nil
}

View File

@@ -18,12 +18,12 @@ type CreateTemplateRequest struct {
// Name to assign to the template being created // Name to assign to the template being created
// Required: true // Required: true
Name string `url:"name" json:"name" validate:"required"` Name string `url:"name" json:"name" validate:"required"`
}
// Async API call type wrapperCreateTemplateRequest struct {
// For async call use CreateTemplateAsync CreateTemplateRequest
// For sync call use CreateTemplate
// Required: true Async bool `url:"async"`
async bool `url:"async"`
} }
// CreateTemplate create template from compute instance // CreateTemplate create template from compute instance
@@ -35,11 +35,14 @@ func (c Compute) CreateTemplate(ctx context.Context, req CreateTemplateRequest)
} }
} }
req.async = false reqWrapped := wrapperCreateTemplateRequest{
CreateTemplateRequest: req,
Async: false,
}
url := "/cloudapi/compute/createTemplate" url := "/cloudapi/compute/createTemplate"
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped)
if err != nil { if err != nil {
return 0, err return 0, err
} }
@@ -61,11 +64,14 @@ func (c Compute) CreateTemplateAsync(ctx context.Context, req CreateTemplateRequ
} }
} }
req.async = true reqWrapped := wrapperCreateTemplateRequest{
CreateTemplateRequest: req,
Async: true,
}
url := "/cloudapi/compute/createTemplate" url := "/cloudapi/compute/createTemplate"
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, reqWrapped)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@@ -0,0 +1,40 @@
package compute
import (
"context"
"net/http"
"strconv"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// Request struct for deleting compute's custome fields
type DeleteCustomFieldsRequest struct {
// ID of the compute
// Required: true
ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"`
}
// DeleteCustomFields deletes computes custom fields
func (c Compute) DeleteCustomFields(ctx context.Context, req DeleteCustomFieldsRequest) (bool, error) {
err := validators.ValidateRequest(req)
if err != nil {
for _, validationError := range validators.GetErrors(err) {
return false, validators.ValidationError(validationError)
}
}
url := "/cloudapi/compute/deleteCustomFields"
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil {
return false, err
}
result, err := strconv.ParseBool(string(res))
if err != nil {
return false, err
}
return result, nil
}

View File

@@ -17,6 +17,10 @@ type DiskAttachRequest struct {
// ID of the disk to attach // ID of the disk to attach
// Required: true // Required: true
DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` DiskID uint64 `url:"diskId" json:"diskId" validate:"required"`
// Type of the disk B;D
// Required: false
DiskType string `url:"diskType,omitempty" json:"diskType,omitempty" validate:"omitempty,computeDiskType"`
} }
// DiskAttach attach disk to compute // DiskAttach attach disk to compute

View File

@@ -20,7 +20,7 @@ type DiskDelRequest struct {
// False if disk is to be deleted to recycle bin // False if disk is to be deleted to recycle bin
// Required: true // Required: true
Permanently bool `url:"permanently" json:"permanently" validate:"required"` Permanently bool `url:"permanently" json:"permanently"`
} }
// DiskDel delete disk and detach from compute // DiskDel delete disk and detach from compute

View File

@@ -59,7 +59,7 @@ func (lc ListComputes) FilterByDiskID(diskID uint64) ListComputes {
} }
// FilterByK8SID returns master and worker nodes (ListComputes) inside specified K8S cluster. // FilterByK8SID returns master and worker nodes (ListComputes) inside specified K8S cluster.
func (lc ListComputes) FilterByK8SID(ctx context.Context, k8sID uint64, decortClient interfaces.Caller) (ListComputes, error) { func (lc ListComputes) FilterByK8SID(ctx context.Context, k8sID uint64, decortClient interfaces.Caller) (*ListComputes, error) {
caller := k8s.New(decortClient) caller := k8s.New(decortClient)
req := k8s.GetRequest{ req := k8s.GetRequest{
@@ -89,7 +89,9 @@ func (lc ListComputes) FilterByK8SID(ctx context.Context, k8sID uint64, decortCl
return false return false
} }
return lc.FilterFunc(predicate), nil res := lc.FilterFunc(predicate)
return &res, nil
} }
// K8SMasters is used to filter master nodes. Best used after FilterByK8SID function. // K8SMasters is used to filter master nodes. Best used after FilterByK8SID function.
@@ -121,7 +123,7 @@ func (lc ListComputes) FilterByK8SWorkers() ListComputes {
} }
// FilterByLBID returns ListComputes used by specified Load Balancer. // FilterByLBID returns ListComputes used by specified Load Balancer.
func (lc ListComputes) FilterByLBID(ctx context.Context, lbID uint64, decortClient interfaces.Caller) (ListComputes, error) { func (lc ListComputes) FilterByLBID(ctx context.Context, lbID uint64, decortClient interfaces.Caller) (*ListComputes, error) {
caller := lb.New(decortClient) caller := lb.New(decortClient)
req := lb.GetRequest{ req := lb.GetRequest{
@@ -137,28 +139,32 @@ func (lc ListComputes) FilterByLBID(ctx context.Context, lbID uint64, decortClie
return ic.ID == foundLB.PrimaryNode.ComputeID || ic.ID == foundLB.SecondaryNode.ComputeID return ic.ID == foundLB.PrimaryNode.ComputeID || ic.ID == foundLB.SecondaryNode.ComputeID
} }
return lc.FilterFunc(predicate), nil res := lc.FilterFunc(predicate)
return &res, nil
} }
// FilterFunc allows filtering ListComputes based on a user-specified predicate. // FilterFunc allows filtering ListComputes based on a user-specified predicate.
func (lc ListComputes) FilterFunc(predicate func(ItemCompute) bool) ListComputes { func (lc ListComputes) FilterFunc(predicate func(ItemCompute) bool) ListComputes {
var result ListComputes var result ListComputes
for _, item := range lc { for _, item := range lc.Data {
if predicate(item) { if predicate(item) {
result = append(result, item) result.Data = append(result.Data, item)
} }
} }
result.EntryCount = uint64(len(result.Data))
return result return result
} }
// FindOne returns first found ItemCompute // FindOne returns first found ItemCompute
// If none was found, returns an empty struct. // If none was found, returns an empty struct.
func (lc ListComputes) FindOne() ItemCompute { func (lc ListComputes) FindOne() ItemCompute {
if len(lc) == 0 { if len(lc.Data) == 0 {
return ItemCompute{} return ItemCompute{}
} }
return lc[0] return lc.Data[0]
} }

View File

@@ -3,239 +3,242 @@ package compute
import "testing" import "testing"
var computes = ListComputes{ var computes = ListComputes{
ItemCompute{ Data: []ItemCompute{
ACL: []interface{}{}, {
AccountID: 132847, ACL: ListACL{},
AccountName: "std_2", AccountID: 132847,
AffinityLabel: "", AccountName: "std_2",
AffinityRules: []ItemRule{ AffinityLabel: "",
{ AffinityRules: []ItemRule{
GUID: "", {
Key: "aff_key", GUID: "",
Mode: "ANY", Key: "aff_key",
Policy: "RECOMMENDED", Mode: "ANY",
Topology: "compute", Policy: "RECOMMENDED",
Value: "aff_val", Topology: "compute",
Value: "aff_val",
},
}, },
}, AffinityWeight: 0,
AffinityWeight: 0, AntiAffinityRules: []ItemRule{
AntiAffinityRules: []ItemRule{ {
{ GUID: "",
GUID: "", Key: "antiaff_key",
Key: "antiaff_key", Mode: "ANY",
Mode: "ANY", Policy: "RECOMMENDED",
Policy: "RECOMMENDED", Topology: "compute",
Topology: "compute", Value: "antiaff_val",
Value: "antiaff_val", },
}, },
}, Architecture: "X86_64",
Architecture: "X86_64", BootOrder: []string{
BootOrder: []string{ "hd", "cdrom",
"hd", "cdrom",
},
BootDiskSize: 0,
CloneReference: 0,
Clones: []uint64{},
ComputeCIID: 0,
CPU: 4,
CreatedBy: "timofey_tkachev_1@decs3o",
CreatedTime: 1676975175,
CustomFields: map[string]interface{}{},
DeletedBy: "",
DeletedTime: 0,
Description: "",
Devices: nil,
Disks: []InfoDisk{
{
ID: 65191,
PCISlot: 6,
}, },
BootDiskSize: 0,
CloneReference: 0,
Clones: []uint64{},
ComputeCIID: 0,
CPU: 4,
CreatedBy: "timofey_tkachev_1@decs3o",
CreatedTime: 1676975175,
CustomFields: map[string]interface{}{},
DeletedBy: "",
DeletedTime: 0,
Description: "",
Devices: nil,
Disks: []InfoDisk{
{
ID: 65191,
PCISlot: 6,
},
},
Driver: "KVM_X86",
GID: 212,
GUID: 48500,
ID: 48500,
ImageID: 9884,
Interfaces: []ItemVNFInterface{},
LockStatus: "UNLOCKED",
ManagerID: 0,
ManagerType: "",
MigrationJob: 0,
Milestones: 363500,
Name: "test",
Pinned: false,
RAM: 4096,
ReferenceID: "c7cb19ac-af4a-4067-852f-c5572949207e",
Registered: true,
ResName: "compute-48500",
RGID: 79724,
RGName: "std_broker2",
SnapSets: []ItemSnapSet{},
StatelessSepID: 0,
StatelessSepType: "",
Status: "ENABLED",
Tags: map[string]string{},
TechStatus: "STOPPED",
TotalDiskSize: 2,
UpdatedBy: "",
UpdatedTime: 1677058904,
UserManaged: true,
VGPUs: []uint64{},
VINSConnected: 0,
VirtualImageID: 0,
},
{
ACL: ListACL{},
AccountID: 132848,
AccountName: "std_broker",
AffinityLabel: "",
AffinityRules: []ItemRule{},
AffinityWeight: 0,
AntiAffinityRules: []ItemRule{},
Architecture: "X86_64",
BootOrder: []string{
"hd", "cdrom",
},
BootDiskSize: 0,
CloneReference: 0,
Clones: []uint64{},
ComputeCIID: 0,
CPU: 6,
CreatedBy: "timofey_tkachev_1@decs3o",
CreatedTime: 1677579436,
CustomFields: map[string]interface{}{},
DeletedBy: "",
DeletedTime: 0,
Description: "",
Devices: nil,
Disks: []InfoDisk{
{
ID: 65248,
PCISlot: 6,
},
},
Driver: "KVM_X86",
GID: 212,
GUID: 48556,
ID: 48556,
ImageID: 9884,
Interfaces: []ItemVNFInterface{},
LockStatus: "UNLOCKED",
ManagerID: 0,
ManagerType: "",
MigrationJob: 0,
Milestones: 363853,
Name: "compute_2",
Pinned: false,
RAM: 4096,
ReferenceID: "a542c449-5b1c-4f90-88c5-7bb5f8ae68ff",
Registered: true,
ResName: "compute-48556",
RGID: 79727,
RGName: "sdk_negative_fields_test",
SnapSets: []ItemSnapSet{},
StatelessSepID: 0,
StatelessSepType: "",
Status: "ENABLED",
Tags: map[string]string{},
TechStatus: "STARTED",
TotalDiskSize: 1,
UpdatedBy: "",
UpdatedTime: 1677579436,
UserManaged: true,
VGPUs: []uint64{},
VINSConnected: 0,
VirtualImageID: 0,
}, },
Driver: "KVM_X86",
GID: 212,
GUID: 48500,
ID: 48500,
ImageID: 9884,
Interfaces: []ItemVNFInterface{},
LockStatus: "UNLOCKED",
ManagerID: 0,
ManagerType: "",
MigrationJob: 0,
Milestones: 363500,
Name: "test",
Pinned: false,
RAM: 4096,
ReferenceID: "c7cb19ac-af4a-4067-852f-c5572949207e",
Registered: true,
ResName: "compute-48500",
RGID: 79724,
RGName: "std_broker2",
SnapSets: []ItemSnapSet{},
StatelessSepID: 0,
StatelessSepType: "",
Status: "ENABLED",
Tags: map[string]string{},
TechStatus: "STOPPED",
TotalDiskSize: 2,
UpdatedBy: "",
UpdatedTime: 1677058904,
UserManaged: true,
VGPUs: []uint64{},
VINSConnected: 0,
VirtualImageID: 0,
},
ItemCompute{
ACL: []interface{}{},
AccountID: 132848,
AccountName: "std_broker",
AffinityLabel: "",
AffinityRules: []ItemRule{},
AffinityWeight: 0,
AntiAffinityRules: []ItemRule{},
Architecture: "X86_64",
BootOrder: []string{
"hd", "cdrom",
},
BootDiskSize: 0,
CloneReference: 0,
Clones: []uint64{},
ComputeCIID: 0,
CPU: 6,
CreatedBy: "timofey_tkachev_1@decs3o",
CreatedTime: 1677579436,
CustomFields: map[string]interface{}{},
DeletedBy: "",
DeletedTime: 0,
Description: "",
Devices: nil,
Disks: []InfoDisk{
{
ID: 65248,
PCISlot: 6,
},
},
Driver: "KVM_X86",
GID: 212,
GUID: 48556,
ID: 48556,
ImageID: 9884,
Interfaces: []ItemVNFInterface{},
LockStatus: "UNLOCKED",
ManagerID: 0,
ManagerType: "",
MigrationJob: 0,
Milestones: 363853,
Name: "compute_2",
Pinned: false,
RAM: 4096,
ReferenceID: "a542c449-5b1c-4f90-88c5-7bb5f8ae68ff",
Registered: true,
ResName: "compute-48556",
RGID: 79727,
RGName: "sdk_negative_fields_test",
SnapSets: []ItemSnapSet{},
StatelessSepID: 0,
StatelessSepType: "",
Status: "ENABLED",
Tags: map[string]string{},
TechStatus: "STARTED",
TotalDiskSize: 1,
UpdatedBy: "",
UpdatedTime: 1677579436,
UserManaged: true,
VGPUs: []uint64{},
VINSConnected: 0,
VirtualImageID: 0,
}, },
EntryCount: 2,
} }
func TestFilterByID(t *testing.T) { func TestFilterByID(t *testing.T) {
actual := computes.FilterByID(48500).FindOne() actual := computes.FilterByID(48500).FindOne()
if actual.ID != 48500 { if actual.ID != 48500 {
t.Fatal("expected ID 48500, found: ", actual.ID) t.Fatal("expected ID 48500, found: ", actual.ID)
} }
actualEmpty := computes.FilterByID(0) actualEmpty := computes.FilterByID(0)
if len(actualEmpty) != 0 { if len(actualEmpty.Data) != 0 {
t.Fatal("expected empty, actual: ", len(actualEmpty)) t.Fatal("expected empty, actual: ", len(actualEmpty.Data))
} }
} }
func TestFilterByName(t *testing.T) { func TestFilterByName(t *testing.T) {
actual := computes.FilterByName("test").FindOne() actual := computes.FilterByName("test").FindOne()
if actual.Name != "test" { if actual.Name != "test" {
t.Fatal("expected compute with name 'test', found: ", actual.Name) t.Fatal("expected compute with name 'test', found: ", actual.Name)
} }
} }
func TestFilterByStatus(t *testing.T) { func TestFilterByStatus(t *testing.T) {
actual := computes.FilterByStatus("ENABLED") actual := computes.FilterByStatus("ENABLED")
for _, item := range actual { for _, item := range actual.Data {
if item.Status != "ENABLED" { if item.Status != "ENABLED" {
t.Fatal("expected ENABLED status, found: ", item.Status) t.Fatal("expected ENABLED status, found: ", item.Status)
} }
} }
} }
func TestFilterByTechStatus(t *testing.T) { func TestFilterByTechStatus(t *testing.T) {
actual := computes.FilterByTechStatus("STARTED").FindOne() actual := computes.FilterByTechStatus("STARTED").FindOne()
if actual.ID != 48556 { if actual.ID != 48556 {
t.Fatal("expected 48556 with STARTED techStatus, found: ", actual.ID) t.Fatal("expected 48556 with STARTED techStatus, found: ", actual.ID)
} }
} }
func TestFilterByDiskID(t *testing.T) { func TestFilterByDiskID(t *testing.T) {
actual := computes.FilterByDiskID(65248).FindOne() actual := computes.FilterByDiskID(65248).FindOne()
if actual.ID != 48556 { if actual.ID != 48556 {
t.Fatal("expected 48556 with DiskID 65248, found: ", actual.ID) t.Fatal("expected 48556 with DiskID 65248, found: ", actual.ID)
} }
} }
func TestFilterFunc(t *testing.T) { func TestFilterFunc(t *testing.T) {
actual := computes.FilterFunc(func(ic ItemCompute) bool { actual := computes.FilterFunc(func(ic ItemCompute) bool {
return ic.Registered == true return ic.Registered == true
}) })
if len(actual) != 2 { if len(actual.Data) != 2 {
t.Fatal("expected 2 elements found, actual: ", len(actual)) t.Fatal("expected 2 elements found, actual: ", len(actual.Data))
} }
for _, item := range actual { for _, item := range actual.Data {
if item.Registered != true { if item.Registered != true {
t.Fatal("expected Registered to be true, actual: ", item.Registered) t.Fatal("expected Registered to be true, actual: ", item.Registered)
} }
} }
} }
func TestSortingByCreatedTime(t *testing.T) { func TestSortingByCreatedTime(t *testing.T) {
actual := computes.SortByCreatedTime(false) actual := computes.SortByCreatedTime(false)
if actual[0].Name != "test" { if actual.Data[0].Name != "test" {
t.Fatal("expected 'test', found: ", actual[0].Name) t.Fatal("expected 'test', found: ", actual.Data[0].Name)
} }
actual = computes.SortByCreatedTime(true) actual = computes.SortByCreatedTime(true)
if actual[0].Name != "compute_2" { if actual.Data[0].Name != "compute_2" {
t.Fatal("expected 'compute_2', found: ", actual[0].Name) t.Fatal("expected 'compute_2', found: ", actual.Data[0].Name)
} }
} }
func TestSortingByCPU(t *testing.T) { func TestSortingByCPU(t *testing.T) {
actual := computes.SortByCPU(false) actual := computes.SortByCPU(false)
if actual[0].CPU != 4{ if actual.Data[0].CPU != 4 {
t.Fatal("expected 4 CPU cores, found: ", actual[0].CPU) t.Fatal("expected 4 CPU cores, found: ", actual.Data[0].CPU)
} }
actual = computes.SortByCPU(true) actual = computes.SortByCPU(true)
if actual[0].CPU != 6 { if actual.Data[0].CPU != 6 {
t.Fatal("expected 6 CPU cores, found: ", actual[0].CPU) t.Fatal("expected 6 CPU cores, found: ", actual.Data[0].CPU)
} }
} }

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 for get information about compute // GetRequest struct to get information about compute
type GetRequest struct { type GetRequest struct {
// ID of compute instance // ID of compute instance
// Required: true // Required: true
ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"`
} }
// Get Gets information about compute // Get gets information about compute as a RecordCompute struct
func (c Compute) Get(ctx context.Context, req GetRequest) (*RecordCompute, error) { func (c Compute) Get(ctx context.Context, req GetRequest) (*RecordCompute, error) {
err := validators.ValidateRequest(req) res, err := c.GetRaw(ctx, req)
if err != nil {
for _, validationError := range validators.GetErrors(err) {
return nil, validators.ValidationError(validationError)
}
}
url := "/cloudapi/compute/get"
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -40,3 +31,18 @@ func (c Compute) Get(ctx context.Context, req GetRequest) (*RecordCompute, error
return &info, nil return &info, nil
} }
// GetRaw gets information about compute as an array of bytes
func (c Compute) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) {
err := validators.ValidateRequest(req)
if err != nil {
for _, validationError := range validators.GetErrors(err) {
return nil, validators.ValidationError(validationError)
}
}
url := "/cloudapi/compute/get"
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req)
return res, err
}

View File

@@ -8,19 +8,15 @@ import (
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators" "repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
) )
// Request struct for get list GPU for compute // Request struct for getting Compute's customFields
type ListGPURequest struct { type GetCustomFieldsRequest struct {
// ID of compute instance // Compute ID
// Required: true // Required: true
ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"`
// Also list destroyed
// Required: false
ListDestroyed bool `url:"list_destroyed,omitempty" json:"list_destroyed,omitempty"`
} }
// ListVGPU gets list GPU for compute // GetCustomFields gets Compute's customFields
func (c Compute) ListGPU(ctx context.Context, req ListGPURequest) ([]interface{}, error) { func (c Compute) GetCustomFields(ctx context.Context, req GetCustomFieldsRequest) (interface{}, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { for _, validationError := range validators.GetErrors(err) {
@@ -28,19 +24,19 @@ func (c Compute) ListGPU(ctx context.Context, req ListGPURequest) ([]interface{}
} }
} }
url := "/cloudbroker/compute/listGpu" url := "/cloudapi/compute/getCustomFields"
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
list := make([]interface{}, 0) var info interface{}
err = json.Unmarshal(res, &list) err = json.Unmarshal(res, &info)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return list, nil return &info, nil
} }

View File

@@ -6,8 +6,48 @@ import (
"net/http" "net/http"
) )
// Request struct for get list available computes // ListRequest struct to get list of available computes
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 account ID
// Required: false
AccountID uint64 `url:"accountId,omitempty" json:"accountId,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 status
// Required: false
Status string `url:"status,omitempty" json:"status,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"`
// Include deleted computes // Include deleted computes
// Required: false // Required: false
IncludeDeleted bool `url:"includedeleted,omitempty" json:"includedeleted,omitempty"` IncludeDeleted bool `url:"includedeleted,omitempty" json:"includedeleted,omitempty"`
@@ -23,10 +63,8 @@ type ListRequest struct {
// List gets list of the available computes. // List gets list of the available computes.
// Filtering based on status is possible // Filtering based on status is possible
func (c Compute) List(ctx context.Context, req ListRequest) (ListComputes, error) { func (c Compute) List(ctx context.Context, req ListRequest) (*ListComputes, error) {
url := "/cloudapi/compute/list" res, err := c.ListRaw(ctx, req)
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -38,5 +76,13 @@ func (c Compute) List(ctx context.Context, req ListRequest) (ListComputes, error
return nil, err return nil, err
} }
return list, nil return &list, nil
}
// ListRaw gets list of the available computes.
func (c Compute) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) {
url := "/cloudapi/compute/list"
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req)
return res, err
} }

View File

@@ -8,6 +8,42 @@ import (
// Request struct for get deleted computes list // Request struct for get deleted computes list
type ListDeletedRequest struct { type ListDeletedRequest 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 account ID
// Required: false
AccountID uint64 `url:"accountId,omitempty" json:"accountId,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"`
// Page number // Page number
// Required: false // Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"` Page uint64 `url:"page,omitempty" json:"page,omitempty"`
@@ -18,7 +54,7 @@ type ListDeletedRequest struct {
} }
// ListDeleted gets list all deleted computes // ListDeleted gets list all deleted computes
func (c Compute) ListDeleted(ctx context.Context, req ListDeletedRequest) (ListComputes, error) { func (c Compute) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListComputes, error) {
url := "/cloudapi/compute/listDeleted" url := "/cloudapi/compute/listDeleted"
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req)
@@ -33,5 +69,5 @@ func (c Compute) ListDeleted(ctx context.Context, req ListDeletedRequest) (ListC
return nil, err return nil, err
} }
return list, nil return &list, nil
} }

View File

@@ -13,10 +13,34 @@ type ListPCIDeviceRequest struct {
// Identifier compute // Identifier compute
// Required: true // Required: true
ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"`
// Find by resource group ID
// Required: false
RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"`
// Find by device id
// Required: false
DevID uint64 `url:"devId,omitempty" json:"devId,omitempty"`
// Find by name
// Required: false
Name string `url:"name,omitempty" json:"name,omitempty"`
// Find by status
// Required: false
Status string `url:"status,omitempty" json:"status,omitempty"`
// Page number
// Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"`
// Page size
// Required: false
Size uint64 `url:"size,omitempty" json:"size,omitempty"`
} }
// ListPCIDevice gets list PCI device // ListPCIDevice gets list PCI device
func (c Compute) ListPCIDevice(ctx context.Context, req ListPCIDeviceRequest) ([]interface{}, error) { func (c Compute) ListPCIDevice(ctx context.Context, req ListPCIDeviceRequest) (*ListPCIDevices, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { for _, validationError := range validators.GetErrors(err) {
@@ -31,12 +55,12 @@ func (c Compute) ListPCIDevice(ctx context.Context, req ListPCIDeviceRequest) ([
return nil, err return nil, err
} }
list := []interface{}{} list := ListPCIDevices{}
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

@@ -13,10 +13,34 @@ type ListVGPURequest struct {
// Identifier compute // Identifier compute
// Required: true // Required: true
ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"` ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"`
// Find by GPU id
// Required: false
GPUID uint64 `url:"gpuId,omitempty" json:"gpuId,omitempty"`
// Find by type
// Required: false
Type string `url:"type,omitempty" json:"type,omitempty"`
// Find by status
// Required: false
Status string `url:"status,omitempty" json:"status,omitempty"`
// Page number
// Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"`
// Page size
// Required: false
Size uint64 `url:"size,omitempty" json:"size,omitempty"`
// Include deleted computes. If using field 'status', then includedeleted will be ignored
// Required: false
IncludeDeleted bool `url:"includedeleted,omitempty" json:"includedeleted,omitempty"`
} }
// ListVGPU gets list vGPU // ListVGPU gets list vGPU
func (c Compute) ListVGPU(ctx context.Context, req ListVGPURequest) ([]interface{}, error) { func (c Compute) ListVGPU(ctx context.Context, req ListVGPURequest) (*ListVGPUs, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { for _, validationError := range validators.GetErrors(err) {
@@ -24,19 +48,19 @@ func (c Compute) ListVGPU(ctx context.Context, req ListVGPURequest) ([]interface
} }
} }
url := "/cloudapi/compute/listVgpu" url := "/cloudapi/compute/listVGpu"
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req) res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
list := []interface{}{} list := ListVGPUs{}
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

@@ -1,5 +1,7 @@
package compute package compute
import "strconv"
// Access Control List // Access Control List
type RecordACL struct { type RecordACL struct {
// Account ACL list // Account ACL list
@@ -12,10 +14,35 @@ type RecordACL struct {
RGACL ListACL `json:"rgAcl"` RGACL ListACL `json:"rgAcl"`
} }
type ListUsers struct {
// Data
Data RecordACL `json:"data"`
// Entry count
EntryCount uint64 `json:"entryCount"`
}
type Explicit bool
func (e *Explicit) UnmarshalJSON(b []byte) error {
if b[0] == '"' {
b = b[1 : len(b)-1]
}
res, err := strconv.ParseBool(string(b))
if err != nil {
return err
}
*e = Explicit(res)
return nil
}
// ACL information // ACL information
type ItemACL struct { type ItemACL struct {
// Explicit // Explicit
Explicit bool `json:"explicit"` Explicit Explicit `json:"explicit"`
// GUID // GUID
GUID string `json:"guid"` GUID string `json:"guid"`
@@ -70,7 +97,13 @@ type ItemSnapshot struct {
} }
// List of snapshots // List of snapshots
type ListSnapShots []ItemSnapshot type ListSnapShots struct {
// Data
Data []ItemSnapshot `json:"data"`
// Entry count
EntryCount uint64 `json:"entryCount"`
}
// Main information about port forward // Main information about port forward
type ItemPFW struct { type ItemPFW struct {
@@ -97,7 +130,13 @@ type ItemPFW struct {
} }
// List port forwards // List port forwards
type ListPFWs []ItemPFW type ListPFWs struct {
// Data
Data []ItemPFW `json:"data"`
// Entry count
EntryCount uint64 `json:"entryCount"`
}
// Main information about affinity relations // Main information about affinity relations
type RecordAffinityRelations struct { type RecordAffinityRelations struct {
@@ -131,6 +170,9 @@ type RecordNetAttach struct {
// Default GW // Default GW
DefGW string `json:"defGw"` DefGW string `json:"defGw"`
// Enabled
Enabled bool `json:"enabled"`
// FLIPGroup ID // FLIPGroup ID
FLIPGroupID uint64 `json:"flipgroupId"` FLIPGroupID uint64 `json:"flipgroupId"`
@@ -159,7 +201,7 @@ type RecordNetAttach struct {
NetType string `json:"netType"` NetType string `json:"netType"`
// PCI slot // PCI slot
PCISlot uint64 `json:"pciSlot"` PCISlot int64 `json:"pciSlot"`
// QOS // QOS
QOS QOS `json:"qos"` QOS QOS `json:"qos"`
@@ -381,7 +423,7 @@ type RecordCompute struct {
SnapSets ListSnapSets `json:"snapSets"` SnapSets ListSnapSets `json:"snapSets"`
// Stateless SepID // Stateless SepID
StatelessSepID uint64 `json:"statelessSepId"` StatelessSepID int64 `json:"statelessSepId"`
// Stateless SepType // Stateless SepType
StatelessSepType string `json:"statelessSepType"` StatelessSepType string `json:"statelessSepType"`
@@ -464,6 +506,9 @@ type ItemVNFInterface struct {
// Default GW // Default GW
DefGW string `json:"defGw"` DefGW string `json:"defGw"`
// Enabled
Enabled bool `json:"enabled"`
// FLIPGroup ID // FLIPGroup ID
FLIPGroupID uint64 `json:"flipgroupId"` FLIPGroupID uint64 `json:"flipgroupId"`
@@ -492,7 +537,7 @@ type ItemVNFInterface struct {
NetType string `json:"netType"` NetType string `json:"netType"`
// PCI slot // PCI slot
PCISlot uint64 `json:"pciSlot"` PCISlot int64 `json:"pciSlot"`
// QOS // QOS
QOS QOS `json:"qos"` QOS QOS `json:"qos"`
@@ -592,7 +637,7 @@ type ItemComputeDisk struct {
Passwd string `json:"passwd"` Passwd string `json:"passwd"`
// PCI slot // PCI slot
PCISlot uint64 `json:"pciSlot"` PCISlot int64 `json:"pciSlot"`
// Pool // Pool
Pool string `json:"pool"` Pool string `json:"pool"`
@@ -648,6 +693,9 @@ type SnapshotExtend struct {
// Label // Label
Label string `json:"label"` Label string `json:"label"`
// Reference ID
ReferenceID string `json:"referenceId"`
// Resource ID // Resource ID
ResID string `json:"resId"` ResID string `json:"resId"`
@@ -709,8 +757,7 @@ type IOTune struct {
// Main information about compute // Main information about compute
type ItemCompute struct { type ItemCompute struct {
// Access Control List // Access Control List
ACL []interface{} `json:"acl"` ACL ListACL `json:"acl"`
// Account ID // Account ID
AccountID uint64 `json:"accountId"` AccountID uint64 `json:"accountId"`
@@ -835,7 +882,7 @@ type ItemCompute struct {
SnapSets ListSnapSets `json:"snapSets"` SnapSets ListSnapSets `json:"snapSets"`
// Stateless SepID // Stateless SepID
StatelessSepID uint64 `json:"statelessSepId"` StatelessSepID int64 `json:"statelessSepId"`
// Stateless SepType // Stateless SepType
StatelessSepType string `json:"statelessSepType"` StatelessSepType string `json:"statelessSepType"`
@@ -877,8 +924,32 @@ type InfoDisk struct {
ID uint64 `json:"id"` ID uint64 `json:"id"`
// PCISlot // PCISlot
PCISlot uint64 `json:"pciSlot"` PCISlot int64 `json:"pciSlot"`
} }
// List information about computes // List information about computes
type ListComputes []ItemCompute type ListComputes struct {
// Data
Data []ItemCompute `json:"data"`
// Entry count
EntryCount uint64 `json:"entryCount"`
}
// List VGPUs
type ListVGPUs struct {
// Data
Data []interface{} `json:"data"`
// Entry count
EntryCount uint64 `json:"entryCount"`
}
// List PCI devices
type ListPCIDevices struct {
// Data
Data []interface{} `json:"data"`
// Entry count
EntryCount uint64 `json:"entryCount"`
}

View File

@@ -19,8 +19,9 @@ type PFWAddRequest struct {
PublicPortStart uint64 `url:"publicPortStart" json:"publicPortStart" validate:"required"` PublicPortStart uint64 `url:"publicPortStart" json:"publicPortStart" validate:"required"`
// End port number (inclusive) for the ranged rule // End port number (inclusive) for the ranged rule
// Default value: -1
// Required: false // Required: false
PublicPortEnd uint64 `url:"publicPortEnd,omitempty" json:"publicPortEnd,omitempty"` PublicPortEnd int64 `url:"publicPortEnd,omitempty" json:"publicPortEnd,omitempty"`
// Internal base port number // Internal base port number
// Required: true // Required: true

View File

@@ -16,7 +16,7 @@ type PFWListRequest struct {
} }
// PFWList gets compute port forwards list // PFWList gets compute port forwards list
func (c Compute) PFWList(ctx context.Context, req PFWListRequest) (ListPFWs, error) { func (c Compute) PFWList(ctx context.Context, req PFWListRequest) (*ListPFWs, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { for _, validationError := range validators.GetErrors(err) {
@@ -38,5 +38,5 @@ func (c Compute) PFWList(ctx context.Context, req PFWListRequest) (ListPFWs, err
return nil, err return nil, err
} }
return list, nil return &list, nil
} }

View File

@@ -17,16 +17,16 @@ type ResizeRequest struct {
// New CPU count. // New CPU count.
// Pass 0 if no change to CPU count is required // Pass 0 if no change to CPU count is required
// Required: false // Required: false
Force bool `url:"force,omitempty" json:"force,omitempty"` CPU uint64 `url:"cpu,omitempty" json:"cpu,omitempty"`
// New RAM volume in MB. // New RAM volume in MB.
// Pass 0 if no change to RAM volume is required // Pass 0 if no change to RAM volume is required
// Required: false // Required: false
CPU uint64 `url:"cpu,omitempty" json:"cpu,omitempty"` RAM uint64 `url:"ram,omitempty" json:"ram,omitempty"`
// Force compute resize // Force compute resize
// Required: false // Required: false
RAM uint64 `url:"ram,omitempty" json:"ram,omitempty"` Force bool `url:"force,omitempty" json:"force,omitempty"`
} }
// Resize resize compute instance // Resize resize compute instance

View File

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

View File

@@ -0,0 +1,43 @@
package compute
import (
"context"
"net/http"
"strconv"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// Request struct for setting customFields values for the Compute
type SetCustomFieldsRequest struct {
// ID of the compute
// Required: true
ComputeID uint64 `url:"computeId" json:"computeId" validate:"required"`
// Custom fields for Compute. Must be dict.
// Required: true
CustomFields string `url:"customFields" json:"customFields" validate:"required"`
}
// SetCustomFields sets customFields values for the Compute
func (c Compute) SetCustomFields(ctx context.Context, req SetCustomFieldsRequest) (bool, error) {
err := validators.ValidateRequest(req)
if err != nil {
for _, validationError := range validators.GetErrors(err) {
return false, validators.ValidationError(validationError)
}
}
url := "/cloudapi/compute/setCustomFields"
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil {
return false, err
}
result, err := strconv.ParseBool(string(res))
if err != nil {
return false, err
}
return result, nil
}

View File

@@ -16,7 +16,7 @@ type SnapshotListRequest struct {
} }
// SnapshotList gets list compute snapshots // SnapshotList gets list compute snapshots
func (c Compute) SnapshotList(ctx context.Context, req SnapshotListRequest) (ListSnapShots, error) { func (c Compute) SnapshotList(ctx context.Context, req SnapshotListRequest) (*ListSnapShots, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { for _, validationError := range validators.GetErrors(err) {
@@ -38,5 +38,5 @@ func (c Compute) SnapshotList(ctx context.Context, req SnapshotListRequest) (Lis
return nil, err return nil, err
} }
return list, nil return &list, 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 (lc ListComputes) SortByCPU(inverse bool) ListComputes { func (lc ListComputes) SortByCPU(inverse bool) ListComputes {
if len(lc) < 2 { if len(lc.Data) < 2 {
return lc return lc
} }
sort.Slice(lc, func(i, j int) bool { sort.Slice(lc.Data, func(i, j int) bool {
if inverse { if inverse {
return lc[i].CPU > lc[j].CPU return lc.Data[i].CPU > lc.Data[j].CPU
} }
return lc[i].CPU < lc[j].CPU return lc.Data[i].CPU < lc.Data[j].CPU
}) })
return lc return lc
@@ -25,16 +25,16 @@ func (lc ListComputes) SortByCPU(inverse bool) ListComputes {
// //
// If inverse param is set to true, the order is reversed. // If inverse param is set to true, the order is reversed.
func (lc ListComputes) SortByRAM(inverse bool) ListComputes { func (lc ListComputes) SortByRAM(inverse bool) ListComputes {
if len(lc) < 2 { if len(lc.Data) < 2 {
return lc return lc
} }
sort.Slice(lc, func(i, j int) bool { sort.Slice(lc.Data, func(i, j int) bool {
if inverse { if inverse {
return lc[i].RAM > lc[j].RAM return lc.Data[i].RAM > lc.Data[j].RAM
} }
return lc[i].RAM < lc[j].RAM return lc.Data[i].RAM < lc.Data[j].RAM
}) })
return lc return lc
@@ -44,16 +44,16 @@ func (lc ListComputes) SortByRAM(inverse bool) ListComputes {
// //
// If inverse param is set to true, the order is reversed. // If inverse param is set to true, the order is reversed.
func (lc ListComputes) SortByCreatedTime(inverse bool) ListComputes { func (lc ListComputes) SortByCreatedTime(inverse bool) ListComputes {
if len(lc) < 2 { if len(lc.Data) < 2 {
return lc return lc
} }
sort.Slice(lc, func(i, j int) bool { sort.Slice(lc.Data, func(i, j int) bool {
if inverse { if inverse {
return lc[i].CreatedTime > lc[j].CreatedTime return lc.Data[i].CreatedTime > lc.Data[j].CreatedTime
} }
return lc[i].CreatedTime < lc[j].CreatedTime return lc.Data[i].CreatedTime < lc.Data[j].CreatedTime
}) })
return lc return lc
@@ -63,16 +63,16 @@ func (lc ListComputes) SortByCreatedTime(inverse bool) ListComputes {
// //
// If inverse param is set to true, the order is reversed. // If inverse param is set to true, the order is reversed.
func (lc ListComputes) SortByUpdatedTime(inverse bool) ListComputes { func (lc ListComputes) SortByUpdatedTime(inverse bool) ListComputes {
if len(lc) < 2 { if len(lc.Data) < 2 {
return lc return lc
} }
sort.Slice(lc, func(i, j int) bool { sort.Slice(lc.Data, func(i, j int) bool {
if inverse { if inverse {
return lc[i].UpdatedTime > lc[j].UpdatedTime return lc.Data[i].UpdatedTime > lc.Data[j].UpdatedTime
} }
return lc[i].UpdatedTime < lc[j].UpdatedTime return lc.Data[i].UpdatedTime < lc.Data[j].UpdatedTime
}) })
return lc return lc
@@ -82,16 +82,16 @@ func (lc ListComputes) SortByUpdatedTime(inverse bool) ListComputes {
// //
// If inverse param is set to true, the order is reversed. // If inverse param is set to true, the order is reversed.
func (lc ListComputes) SortByDeletedTime(inverse bool) ListComputes { func (lc ListComputes) SortByDeletedTime(inverse bool) ListComputes {
if len(lc) < 2 { if len(lc.Data) < 2 {
return lc return lc
} }
sort.Slice(lc, func(i, j int) bool { sort.Slice(lc.Data, func(i, j int) bool {
if inverse { if inverse {
return lc[i].DeletedTime > lc[j].DeletedTime return lc.Data[i].DeletedTime > lc.Data[j].DeletedTime
} }
return lc[i].DeletedTime < lc[j].DeletedTime return lc.Data[i].DeletedTime < lc.Data[j].DeletedTime
}) })
return lc return lc

View File

@@ -16,7 +16,7 @@ type UserListRequest struct {
} }
// UserList gets users list for compute // UserList gets users list for compute
func (c Compute) UserList(ctx context.Context, req UserListRequest) (*RecordACL, error) { func (c Compute) UserList(ctx context.Context, req UserListRequest) (*ListUsers, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { for _, validationError := range validators.GetErrors(err) {
@@ -31,7 +31,7 @@ func (c Compute) UserList(ctx context.Context, req UserListRequest) (*RecordACL,
return nil, err return nil, err
} }
list := RecordACL{} list := ListUsers{}
err = json.Unmarshal(res, &list) err = json.Unmarshal(res, &list)
if err != nil { if err != nil {

View File

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

View File

@@ -1,18 +0,0 @@
// API Actor for managing ComputeCI. This actor is a final API for admin to manage ComputeCI
package computeci
import (
"repository.basistech.ru/BASIS/decort-golang-sdk/interfaces"
)
// Structure for creating request to computeci
type ComputeCI struct {
client interfaces.Caller
}
// Builder for computeci endpoints
func New(client interfaces.Caller) *ComputeCI {
return &ComputeCI{
client,
}
}

View File

@@ -1,51 +0,0 @@
package computeci
// FilterByID returns ListComputeCI with specified ID.
func (lci ListComputeCI) FilterByID(id uint64) ListComputeCI {
predicate := func(ic ItemComputeCI) bool {
return ic.ID == id
}
return lci.FilterFunc(predicate)
}
// FilterByName returns ListComputeCI with specified Name.
func (lci ListComputeCI) FilterByName(name string) ListComputeCI {
predicate := func(ic ItemComputeCI) bool {
return ic.Name == name
}
return lci.FilterFunc(predicate)
}
// FilterByStatus returns ListComputeCI with specified Status.
func (lci ListComputeCI) FilterByStatus(status string) ListComputeCI {
predicate := func(ic ItemComputeCI) bool {
return ic.Status == status
}
return lci.FilterFunc(predicate)
}
// FilterFunc allows filtering ListComputeCI based on a user-specified predicate.
func (lci ListComputeCI) FilterFunc(predicate func(ItemComputeCI) bool) ListComputeCI {
var result ListComputeCI
for _, item := range lci {
if predicate(item) {
result = append(result, item)
}
}
return result
}
// FindOne returns first found ItemComputeCI
// If none was found, returns an empty struct.
func (lci ListComputeCI) FindOne() ItemComputeCI {
if len(lci) == 0 {
return ItemComputeCI{}
}
return lci[0]
}

View File

@@ -1,95 +0,0 @@
package computeci
import "testing"
var computeciItems = ListComputeCI{
{
CustomFields: map[string]interface{}{},
Description: "",
Drivers: []string{
"KVM_X86",
},
GUID: 1,
ID: 1,
Name: "computeci_1",
Status: "ENABLED",
Template: "",
},
{
CustomFields: map[string]interface{}{},
Description: "",
Drivers: []string{
"KVM_X86",
},
GUID: 2,
ID: 2,
Name: "computeci_2",
Status: "ENABLED",
Template: "",
},
{
CustomFields: map[string]interface{}{},
Description: "",
Drivers: []string{
"SVA_KVM_X86",
},
GUID: 3,
ID: 3,
Name: "computeci_3",
Status: "DISABLED",
Template: "",
},
}
func TestFilterByID(t *testing.T) {
actual := computeciItems.FilterByID(2).FindOne()
if actual.ID != 2 {
t.Fatal("expected ID 2, found: ", actual.ID)
}
}
func TestFilterByName(t *testing.T) {
actual := computeciItems.FilterByName("computeci_3").FindOne()
if actual.Name != "computeci_3" {
t.Fatal("expected Name 'computeci_2', found: ", actual.Name)
}
}
func TestFilterByStatus(t *testing.T) {
actual := computeciItems.FilterByStatus("ENABLED")
if len(actual) != 2 {
t.Fatal("expected 2 found, actual: ", len(actual))
}
for _, item := range actual {
if item.Status != "ENABLED" {
t.Fatal("expected Status 'ENABLED', found: ", item.Status)
}
}
}
func TestFilterFunc(t *testing.T) {
actual := computeciItems.FilterFunc(func(icc ItemComputeCI) bool {
for _, item := range icc.Drivers {
if item == "KVM_X86" {
return true
}
}
return false
})
if len(actual) != 2 {
t.Fatal("expected 2 found, actual: ", len(actual))
}
for _, item := range actual {
for _, driver := range item.Drivers {
if driver != "KVM_X86" {
t.Fatal("expected 'KVM_X86' Driver, found: ", driver)
}
}
}
}

View File

@@ -1,42 +0,0 @@
package computeci
import (
"context"
"encoding/json"
"net/http"
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/validators"
)
// Request struct for information about computeci
type GetRequest struct {
// ID of the Compute CI
// Required: true
ComputeCIID uint64 `url:"computeciId" json:"computeciId" validate:"required"`
}
// Get gets information about computeci by ID
func (c ComputeCI) Get(ctx context.Context, req GetRequest) (*ItemComputeCI, error) {
err := validators.ValidateRequest(req)
if err != nil {
for _, validatonError := range validators.GetErrors(err) {
return nil, validators.ValidationError(validatonError)
}
}
url := "/cloudapi/computeci/get"
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil {
return nil, err
}
info := ItemComputeCI{}
err = json.Unmarshal(res, &info)
if err != nil {
return nil, err
}
return &info, nil
}

View File

@@ -1,41 +0,0 @@
package computeci
import (
"context"
"encoding/json"
"net/http"
)
// Request struct for get list of computeci
type ListRequest struct {
// If true list deleted instances as well
// Required: false
IncludeDeleted bool `url:"includeDeleted,omitempty" json:"includeDeleted,omitempty"`
// Page number
// Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"`
// Page size
// Required: false
Size uint64 `url:"size,omitempty" json:"size,omitempty"`
}
// List gets list of computeci instances
func (c ComputeCI) List(ctx context.Context, req ListRequest) (ListComputeCI, error) {
url := "/cloudapi/computeci/list"
res, err := c.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil {
return nil, err
}
list := ListComputeCI{}
err = json.Unmarshal(res, &list)
if err != nil {
return nil, err
}
return list, nil
}

View File

@@ -1,31 +0,0 @@
package computeci
// Main information about computeci
type ItemComputeCI struct {
// Custom fields
CustomFields map[string]interface{} `json:"customFields"`
// Description
Description string `json:"desc"`
// List drivers
Drivers []string `json:"drivers"`
// GUID
GUID uint64 `json:"guid"`
// ID
ID uint64 `json:"id"`
// Name
Name string `json:"name"`
// Status
Status string `json:"status"`
// Template
Template string `json:"template"`
}
// List of computeci instances
type ListComputeCI []ItemComputeCI

View File

@@ -61,7 +61,7 @@ func (ld ListDisks) FilterByComputeID(computeID uint64) ListDisks {
} }
// FilterByK8SID is used to filter ListDisks by specified K8S cluster. // FilterByK8SID is used to filter ListDisks by specified K8S cluster.
func (ld ListDisks) FilterByK8SID(ctx context.Context, k8sID uint64, decortClient interfaces.Caller) (ListDisks, error) { func (ld ListDisks) FilterByK8SID(ctx context.Context, k8sID uint64, decortClient interfaces.Caller) (*ListDisks, error) {
caller := k8s.New(decortClient) caller := k8s.New(decortClient)
req := k8s.GetRequest{ req := k8s.GetRequest{
@@ -76,20 +76,22 @@ func (ld ListDisks) FilterByK8SID(ctx context.Context, k8sID uint64, decortClien
var result ListDisks var result ListDisks
for _, masterCompute := range cluster.K8SGroups.Masters.DetailedInfo { for _, masterCompute := range cluster.K8SGroups.Masters.DetailedInfo {
result = append(result, ld.FilterByComputeID(masterCompute.ID)...) result.Data = append(result.Data, ld.FilterByComputeID(masterCompute.ID).Data...)
} }
for _, workerGroup := range cluster.K8SGroups.Workers { for _, workerGroup := range cluster.K8SGroups.Workers {
for _, workerCompute := range workerGroup.DetailedInfo { for _, workerCompute := range workerGroup.DetailedInfo {
result = append(result, ld.FilterByComputeID(workerCompute.ID)...) result.Data = append(result.Data, ld.FilterByComputeID(workerCompute.ID).Data...)
} }
} }
return result, nil result.EntryCount = uint64(len(result.Data))
return &result, nil
} }
// FilterByLBID is used to filter ListDisks used by computes inside specified Load Balancer. // FilterByLBID is used to filter ListDisks used by computes inside specified Load Balancer.
func (ld ListDisks) FilterByLBID(ctx context.Context, lbID uint64, decortClient interfaces.Caller) (ListDisks, error) { func (ld ListDisks) FilterByLBID(ctx context.Context, lbID uint64, decortClient interfaces.Caller) (*ListDisks, error) {
caller := lb.New(decortClient) caller := lb.New(decortClient)
req := lb.GetRequest{ req := lb.GetRequest{
@@ -102,15 +104,141 @@ func (ld ListDisks) FilterByLBID(ctx context.Context, lbID uint64, decortClient
} }
var result ListDisks var result ListDisks
result.Data = append(result.Data, ld.FilterByComputeID(foundLB.PrimaryNode.ComputeID).Data...)
result.Data = append(result.Data, ld.FilterByComputeID(foundLB.SecondaryNode.ComputeID).Data...)
result.EntryCount = uint64(len(result.Data))
return &result, nil
}
// FilterFunc allows filtering ListDisks based on a user-specified predicate.
func (ld ListDisks) FilterFunc(predicate func(ItemDisk) bool) ListDisks {
var result ListDisks
for _, item := range ld.Data {
if predicate(item) {
result.Data = append(result.Data, item)
}
}
result.EntryCount = uint64(len(result.Data))
return result
}
// FindOne returns first found ItemDisk
// If none was found, returns an empty struct.
func (ld ListDisks) FindOne() ItemDisk {
if len(ld.Data) == 0 {
return ItemDisk{}
}
return ld.Data[0]
}
// FilterByID returns ListSearchDisks with specified ID.
func (ld ListSearchDisks) FilterByID(id uint64) ListSearchDisks {
predicate := func(idisk ItemDisk) bool {
return idisk.ID == id
}
return ld.FilterFunc(predicate)
}
// FilterByName returns ListSearchDisks with specified Name.
func (ld ListSearchDisks) FilterByName(name string) ListSearchDisks {
predicate := func(idisk ItemDisk) bool {
return idisk.Name == name
}
return ld.FilterFunc(predicate)
}
// FilterByStatus returns ListSearchDisks with specified Status.
func (ld ListSearchDisks) FilterByStatus(status string) ListSearchDisks {
predicate := func(idisk ItemDisk) bool {
return idisk.Status == status
}
return ld.FilterFunc(predicate)
}
// FilterByTechStatus returns ListSearchDisks with specified TechStatus.
func (ld ListSearchDisks) FilterByTechStatus(techStatus string) ListSearchDisks {
predicate := func(idisk ItemDisk) bool {
return idisk.TechStatus == techStatus
}
return ld.FilterFunc(predicate)
}
// FilterByComputeID is used to filter ListSearchDisks attached to specified compute.
func (ld ListSearchDisks) FilterByComputeID(computeID uint64) ListSearchDisks {
predicate := func(idisk ItemDisk) bool {
for k := range idisk.Computes {
if k == strconv.FormatUint(computeID, 10) {
return true
}
}
return false
}
return ld.FilterFunc(predicate)
}
// FilterByK8SID is used to filter ListSearchDisks by specified K8S cluster.
func (ld ListSearchDisks) FilterByK8SID(ctx context.Context, k8sID uint64, decortClient interfaces.Caller) (ListSearchDisks, error) {
caller := k8s.New(decortClient)
req := k8s.GetRequest{
K8SID: k8sID,
}
cluster, err := caller.Get(ctx, req)
if err != nil {
return nil, err
}
var result ListSearchDisks
for _, masterCompute := range cluster.K8SGroups.Masters.DetailedInfo {
result = append(result, ld.FilterByComputeID(masterCompute.ID)...)
}
for _, workerGroup := range cluster.K8SGroups.Workers {
for _, workerCompute := range workerGroup.DetailedInfo {
result = append(result, ld.FilterByComputeID(workerCompute.ID)...)
}
}
return result, nil
}
// FilterByLBID is used to filter ListSearchDisks used by computes inside specified Load Balancer.
func (ld ListSearchDisks) FilterByLBID(ctx context.Context, lbID uint64, decortClient interfaces.Caller) (ListSearchDisks, error) {
caller := lb.New(decortClient)
req := lb.GetRequest{
LBID: lbID,
}
foundLB, err := caller.Get(ctx, req)
if err != nil {
return nil, err
}
var result ListSearchDisks
result = append(result, ld.FilterByComputeID(foundLB.PrimaryNode.ComputeID)...) result = append(result, ld.FilterByComputeID(foundLB.PrimaryNode.ComputeID)...)
result = append(result, ld.FilterByComputeID(foundLB.SecondaryNode.ComputeID)...) result = append(result, ld.FilterByComputeID(foundLB.SecondaryNode.ComputeID)...)
return result, nil return result, nil
} }
// FilterFunc allows filtering ListDisks based on a user-specified predicate. // FilterFunc allows filtering ListSearchDisks based on a user-specified predicate.
func (ld ListDisks) FilterFunc(predicate func(ItemDisk) bool) ListDisks { func (ld ListSearchDisks) FilterFunc(predicate func(ItemDisk) bool) ListSearchDisks {
var result ListDisks var result ListSearchDisks
for _, item := range ld { for _, item := range ld {
if predicate(item) { if predicate(item) {
@@ -123,10 +251,71 @@ func (ld ListDisks) FilterFunc(predicate func(ItemDisk) bool) ListDisks {
// FindOne returns first found ItemDisk // FindOne returns first found ItemDisk
// If none was found, returns an empty struct. // If none was found, returns an empty struct.
func (ld ListDisks) FindOne() ItemDisk { func (ld ListSearchDisks) FindOne() ItemDisk {
if len(ld) == 0 { if len(ld) == 0 {
return ItemDisk{} return ItemDisk{}
} }
return ld[0] return ld[0]
} }
// FilterByID returns ListDisksUnattached with specified ID.
func (lu ListDisksUnattached) FilterByID(id uint64) ListDisksUnattached {
predicate := func(idisk ItemDiskUnattached) bool {
return idisk.ID == id
}
return lu.FilterFunc(predicate)
}
// FilterByName returns ListDisksUnattached with specified Name.
func (lu ListDisksUnattached) FilterByName(name string) ListDisksUnattached {
predicate := func(idisk ItemDiskUnattached) bool {
return idisk.Name == name
}
return lu.FilterFunc(predicate)
}
// FilterByStatus returns ListDisksUnattached with specified Status.
func (lu ListDisksUnattached) FilterByStatus(status string) ListDisksUnattached {
predicate := func(idisk ItemDiskUnattached) bool {
return idisk.Status == status
}
return lu.FilterFunc(predicate)
}
// FilterByTechStatus returns ListDisksUnattached with specified TechStatus.
func (lu ListDisksUnattached) FilterByTechStatus(techStatus string) ListDisksUnattached {
predicate := func(idisk ItemDiskUnattached) bool {
return idisk.TechStatus == techStatus
}
return lu.FilterFunc(predicate)
}
// FilterFunc allows filtering ListDisksUnattached based on a user-specified predicate.
func (lu ListDisksUnattached) FilterFunc(predicate func(ItemDiskUnattached) bool) ListDisksUnattached {
var result ListDisksUnattached
for _, item := range lu.Data {
if predicate(item) {
result.Data = append(result.Data, item)
}
}
result.EntryCount = uint64(len(result.Data))
return result
}
// FindOne returns first found ItemDiskUnattached
// If none was found, returns an empty struct.
func (lu ListDisksUnattached) FindOne() ItemDiskUnattached {
if len(lu.Data) == 0 {
return ItemDiskUnattached{}
}
return lu.Data[0]
}

View File

@@ -1,8 +1,189 @@
package disks package disks
import "testing" import (
"testing"
)
var techStatusAllocated = "ALLOCATED"
var disks = ListDisks{ var disks = ListDisks{
Data: []ItemDisk{
{
MachineID: 0,
MachineName: "",
DeviceName: "vda",
AccountID: 132847,
AccountName: "std_2",
ACL: map[string]interface{}{},
Computes: map[string]string{
"48500": "test",
},
CreatedTime: 1676975177,
DeletedTime: 0,
Description: "",
DestructionTime: 0,
GID: 212,
ID: 65191,
ImageID: 9884,
Images: []uint64{},
IOTune: IOTune{
TotalIOPSSec: 2000,
},
Name: "bootdisk",
Order: 0,
Params: "",
ParentID: 0,
PCISlot: 6,
Pool: "vmstor",
PresentTo: []uint64{
27,
},
PurgeTime: 0,
ResID: "sample",
ResName: "sample",
Role: "",
Shareable: false,
SizeMax: 2,
SizeUsed: 2,
Snapshots: []ItemSnapshot{},
Status: "ASSIGNED",
TechStatus: techStatusAllocated,
Type: "B",
VMID: 48500,
},
{
MachineID: 0,
MachineName: "",
DeviceName: "vda",
AccountID: 132852,
AccountName: "std",
ACL: map[string]interface{}{},
Computes: map[string]string{
"48502": "stdvm2",
},
CreatedTime: 1676982606,
DeletedTime: 0,
Description: "",
DestructionTime: 0,
GID: 212,
ID: 65193,
ImageID: 9885,
Images: []uint64{},
IOTune: IOTune{
TotalIOPSSec: 2000,
},
Name: "bootdisk",
Order: 0,
Params: "",
ParentID: 0,
PCISlot: 6,
Pool: "vmstor",
PresentTo: []uint64{
27,
27,
},
PurgeTime: 0,
ResID: "sample",
ResName: "sample",
Role: "",
Shareable: false,
SizeMax: 4,
SizeUsed: 4,
Snapshots: []ItemSnapshot{},
Status: "ASSIGNED",
TechStatus: techStatusAllocated,
Type: "B",
VMID: 48502,
},
},
EntryCount: 2,
}
func TestListDisks_FilterByID(t *testing.T) {
actual := disks.FilterByID(65193)
if len(actual.Data) == 0 {
t.Fatal("No elements were found")
}
actualItem := actual.FindOne()
if actualItem.ID != 65193 {
t.Fatal("expected ID 65193, found: ", actualItem.ID)
}
}
func TestListDisks_FilterByName(t *testing.T) {
actual := disks.FilterByName("bootdisk")
if len(actual.Data) != 2 {
t.Fatal("expected 2 elements, found: ", len(actual.Data))
}
for _, item := range actual.Data {
if item.Name != "bootdisk" {
t.Fatal("expected 'bootdisk' name, found: ", item.Name)
}
}
}
func TestListDisks_FilterByStatus(t *testing.T) {
actual := disks.FilterByStatus("ASSIGNED")
if len(actual.Data) == 0 {
t.Fatal("No elements were found")
}
for _, item := range actual.Data {
if item.Status != "ASSIGNED" {
t.Fatal("expected 'ASSIGNED' status, found: ", item.Status)
}
}
}
func TestListDisks_FilterByTechStatus(t *testing.T) {
actual := disks.FilterByTechStatus(techStatusAllocated)
if len(actual.Data) == 0 {
t.Fatal("No elements were found")
}
for _, item := range actual.Data {
if item.TechStatus != techStatusAllocated {
t.Fatal("expected 'ALLOCATED' techStatus, found: ", item.TechStatus)
}
}
}
func TestListDisks_FilterFunc(t *testing.T) {
actual := disks.FilterFunc(func(id ItemDisk) bool {
return len(id.PresentTo) == 2
})
if len(actual.Data) == 0 {
t.Fatal("No elements were found")
}
if len(actual.Data[0].PresentTo) != 2 {
t.Fatal("expected 2 elements in PresentTo, found: ", len(actual.Data[0].PresentTo))
}
}
func TestListDisks_SortByCreatedTime(t *testing.T) {
actual := disks.SortByCreatedTime(false)
if actual.Data[0].ID != 65191 {
t.Fatal("expected ID 65191, found: ", actual.Data[0].ID)
}
actual = disks.SortByCreatedTime(true)
if actual.Data[0].ID != 65193 {
t.Fatal("expected ID 65193, found: ", actual.Data[0].ID)
}
}
var searchDisks = ListSearchDisks{
ItemDisk{ ItemDisk{
MachineID: 0, MachineID: 0,
MachineName: "", MachineName: "",
@@ -42,7 +223,7 @@ var disks = ListDisks{
SizeUsed: 2, SizeUsed: 2,
Snapshots: []ItemSnapshot{}, Snapshots: []ItemSnapshot{},
Status: "ASSIGNED", Status: "ASSIGNED",
TechStatus: "ALLOCATED", TechStatus: techStatusAllocated,
Type: "B", Type: "B",
VMID: 48500, VMID: 48500,
}, },
@@ -86,14 +267,14 @@ var disks = ListDisks{
SizeUsed: 4, SizeUsed: 4,
Snapshots: []ItemSnapshot{}, Snapshots: []ItemSnapshot{},
Status: "ASSIGNED", Status: "ASSIGNED",
TechStatus: "ALLOCATED", TechStatus: techStatusAllocated,
Type: "B", Type: "B",
VMID: 48502, VMID: 48502,
}, },
} }
func TestFilterByID(t *testing.T) { func TestListSearchDisks_FilterByID(t *testing.T) {
actual := disks.FilterByID(65193) actual := searchDisks.FilterByID(65193)
if len(actual) == 0 { if len(actual) == 0 {
t.Fatal("No elements were found") t.Fatal("No elements were found")
@@ -106,8 +287,8 @@ func TestFilterByID(t *testing.T) {
} }
} }
func TestFilterByName(t *testing.T) { func TestListSearchDisks_FilterByName(t *testing.T) {
actual := disks.FilterByName("bootdisk") actual := searchDisks.FilterByName("bootdisk")
if len(actual) != 2 { if len(actual) != 2 {
t.Fatal("expected 2 elements, found: ", len(actual)) t.Fatal("expected 2 elements, found: ", len(actual))
@@ -120,8 +301,8 @@ func TestFilterByName(t *testing.T) {
} }
} }
func TestFilterByStatus(t *testing.T) { func TestListSearchDisks_FilterByStatus(t *testing.T) {
actual := disks.FilterByStatus("ASSIGNED") actual := searchDisks.FilterByStatus("ASSIGNED")
if len(actual) == 0 { if len(actual) == 0 {
t.Fatal("No elements were found") t.Fatal("No elements were found")
@@ -134,22 +315,22 @@ func TestFilterByStatus(t *testing.T) {
} }
} }
func TestFilterByTechStatus(t *testing.T) { func TestListSearchDisks_FilterByTechStatus(t *testing.T) {
actual := disks.FilterByTechStatus("ALLOCATED") actual := searchDisks.FilterByTechStatus(techStatusAllocated)
if len(actual) == 0 { if len(actual) == 0 {
t.Fatal("No elements were found") t.Fatal("No elements were found")
} }
for _, item := range actual { for _, item := range actual {
if item.TechStatus != "ALLOCATED" { if item.TechStatus != techStatusAllocated {
t.Fatal("expected 'ALLOCATED' techStatus, found: ", item.TechStatus) t.Fatal("expected 'ALLOCATED' techStatus, found: ", item.TechStatus)
} }
} }
} }
func TestFilterFunc(t *testing.T) { func TestListSearchDisks_FilterFunc(t *testing.T) {
actual := disks.FilterFunc(func(id ItemDisk) bool { actual := searchDisks.FilterFunc(func(id ItemDisk) bool {
return len(id.PresentTo) == 2 return len(id.PresentTo) == 2
}) })
@@ -162,16 +343,214 @@ func TestFilterFunc(t *testing.T) {
} }
} }
func TestSortByCreatedTime(t *testing.T) { func TestListSearchDisks_SortByCreatedTime(t *testing.T) {
actual := disks.SortByCreatedTime(false) actual := searchDisks.SortByCreatedTime(false)
if actual[0].ID != 65191 { if actual[0].ID != 65191 {
t.Fatal("expected ID 65191, found: ", actual[0].ID) t.Fatal("expected ID 65191, found: ", actual[0].ID)
} }
actual = disks.SortByCreatedTime(true) actual = searchDisks.SortByCreatedTime(true)
if actual[0].ID != 65193 { if actual[0].ID != 65193 {
t.Fatal("expected ID 65193, found: ", actual[0].ID) t.Fatal("expected ID 65193, found: ", actual[0].ID)
} }
} }
var unattachedDisks = ListDisksUnattached{
Data: []ItemDiskUnattached{
{
CKey: "",
Meta: []interface{}{
"cloudbroker",
"disk",
1,
},
AccountID: 149,
AccountName: "test_account1",
ACL: map[string]interface{}{},
BootPartition: 0,
CreatedTime: 1681477547,
DeletedTime: 0,
Description: "",
DestructionTime: 0,
DiskPath: "",
GID: 2002,
GUID: 22636,
ID: 22636,
ImageID: 0,
Images: []uint64{},
IOTune: IOTune{
TotalIOPSSec: 2000,
},
IQN: "",
Login: "",
Milestones: 43834,
Name: "test_disk",
Order: 0,
Params: "",
ParentID: 0,
Password: "",
PCISlot: -1,
Pool: "data05",
PresentTo: []uint64{},
PurgeAttempts: 0,
PurgeTime: 0,
RealityDeviceNumber: 0,
ReferenceID: "",
ResID: "79bd3bd8-3424-48d3-963f-1870d506f169",
ResName: "volumes/volume_22636",
Role: "",
SEPID: 1,
Shareable: false,
SizeMax: 0,
SizeUsed: 0,
Snapshots: nil,
Status: "CREATED",
TechStatus: techStatusAllocated,
Type: "D",
VMID: 0,
},
{
CKey: "",
Meta: []interface{}{
"cloudbroker",
"disk",
1,
},
AccountID: 150,
AccountName: "test_account",
ACL: map[string]interface{}{},
BootPartition: 0,
CreatedTime: 1681477558,
DeletedTime: 0,
Description: "",
DestructionTime: 0,
DiskPath: "",
GID: 2002,
GUID: 22637,
ID: 22637,
ImageID: 0,
Images: []uint64{},
IOTune: IOTune{
TotalIOPSSec: 2000,
},
IQN: "",
Login: "",
Milestones: 43834,
Name: "test_disk",
Order: 0,
Params: "",
ParentID: 0,
Password: "",
PCISlot: -1,
Pool: "data05",
PresentTo: []uint64{
27,
27,
},
PurgeAttempts: 0,
PurgeTime: 0,
RealityDeviceNumber: 0,
ReferenceID: "",
ResID: "79bd3bd8-3424-48d3-963f-1870d506f169",
ResName: "volumes/volume_22637",
Role: "",
SEPID: 1,
Shareable: false,
SizeMax: 0,
SizeUsed: 0,
Snapshots: nil,
Status: "CREATED",
TechStatus: techStatusAllocated,
Type: "B",
VMID: 0,
},
},
EntryCount: 2,
}
func TestListDisksUnattached_FilterByID(t *testing.T) {
actual := unattachedDisks.FilterByID(22636)
if len(actual.Data) == 0 {
t.Fatal("No elements were found")
}
actualItem := actual.FindOne()
if actualItem.ID != 22636 {
t.Fatal("expected ID 22636, found: ", actualItem.ID)
}
}
func TestListDisksUnattached_FilterByName(t *testing.T) {
actual := unattachedDisks.FilterByName("test_disk")
if len(actual.Data) != 2 {
t.Fatal("expected 2 elements, found: ", len(actual.Data))
}
for _, item := range actual.Data {
if item.Name != "test_disk" {
t.Fatal("expected 'test_disk' name, found: ", item.Name)
}
}
}
func TestListDisksUnattached_FilterByStatus(t *testing.T) {
actual := unattachedDisks.FilterByStatus("CREATED")
if len(actual.Data) == 0 {
t.Fatal("No elements were found")
}
for _, item := range actual.Data {
if item.Status != "CREATED" {
t.Fatal("expected 'CREATED' status, found: ", item.Status)
}
}
}
func TestListDisksUnattached_FilterByTechStatus(t *testing.T) {
actual := unattachedDisks.FilterByTechStatus(techStatusAllocated)
if len(actual.Data) == 0 {
t.Fatal("No elements were found")
}
for _, item := range actual.Data {
if item.TechStatus != techStatusAllocated {
t.Fatal("expected 'ALLOCATED' techStatus, found: ", item.TechStatus)
}
}
}
func TestListDisksUnattached_FilterFunc(t *testing.T) {
actual := unattachedDisks.FilterFunc(func(id ItemDiskUnattached) bool {
return len(id.PresentTo) == 2
})
if len(actual.Data) == 0 {
t.Fatal("No elements were found")
}
if len(actual.Data[0].PresentTo) != 2 {
t.Fatal("expected 2 elements in PresentTo, found: ", len(actual.Data[0].PresentTo))
}
}
func TestListDisksUnattached_SortByCreatedTime(t *testing.T) {
actual := unattachedDisks.SortByCreatedTime(false)
if actual.Data[0].ID != 22636 {
t.Fatal("expected ID 22636, found: ", actual.Data[0].ID)
}
actual = unattachedDisks.SortByCreatedTime(true)
if actual.Data[0].ID != 22637 {
t.Fatal("expected ID 22637, found: ", actual.Data[0].ID)
}
}

View File

@@ -8,26 +8,17 @@ 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 disk // GetRequest struct to get information about disk
type GetRequest struct { type GetRequest struct {
// ID of the disk // ID of the disk
// Required: true // Required: true
DiskID uint64 `url:"diskId" json:"diskId" validate:"required"` DiskID uint64 `url:"diskId" json:"diskId" validate:"required"`
} }
// Get gets disk details // Get gets disk details as a RecordDisk struct
// Notice: the devicename field is the name as it is passed to the kernel (kname in linux) for unattached disks this field has no relevant value // Notice: the devicename field is the name as it is passed to the kernel (kname in linux) for unattached disks this field has no relevant value
func (d Disks) Get(ctx context.Context, req GetRequest) (*RecordDisk, error) { func (d Disks) Get(ctx context.Context, req GetRequest) (*RecordDisk, error) {
err := validators.ValidateRequest(req) res, err := d.GetRaw(ctx, req)
if err != nil {
for _, validationError := range validators.GetErrors(err) {
return nil, validators.ValidationError(validationError)
}
}
url := "/cloudapi/disks/get"
res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -41,3 +32,19 @@ func (d Disks) Get(ctx context.Context, req GetRequest) (*RecordDisk, error) {
return &info, nil return &info, nil
} }
// GetRaw gets disk details as an array of bytes
// Notice: the devicename field is the name as it is passed to the kernel (kname in linux) for unattached disks this field has no relevant value
func (d Disks) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) {
err := validators.ValidateRequest(req)
if err != nil {
for _, validationError := range validators.GetErrors(err) {
return nil, validators.ValidationError(validationError)
}
}
url := "/cloudapi/disks/get"
res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req)
return res, err
}

View File

@@ -6,8 +6,32 @@ import (
"net/http" "net/http"
) )
// Request struct for get list/list_deleted of disks // ListRequest struct to get list of disks
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 account name
// Required: false
AccountName string `url:"accountName,omitempty" json:"accountName,omitempty"`
// Find by max size disk
// Required: false
DiskMaxSize int64 `url:"diskMaxSize,omitempty" json:"diskMaxSize,omitempty"`
// Find by status
// Required: false
Status string `url:"status,omitempty" json:"status,omitempty"`
// Find by shared, true or false
// Required: false
Shared bool `url:"shared,omitempty" json:"shared,omitempty"`
// ID of the account the disks belong to // ID of the account the disks belong to
// Required: false // Required: false
AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"`
@@ -16,6 +40,14 @@ type ListRequest struct {
// Required: false // Required: false
Type string `url:"type,omitempty" json:"type,omitempty"` Type string `url:"type,omitempty" json:"type,omitempty"`
// Find by sep ID
// Required: false
SEPID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"`
// Find by pool name
// Required: false
Pool string `url:"pool,omitempty" json:"pool,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 +57,27 @@ type ListRequest struct {
Size uint64 `url:"size,omitempty" json:"size,omitempty"` Size uint64 `url:"size,omitempty" json:"size,omitempty"`
} }
// List gets list the created disks belonging to an account // List gets list of the created disks belonging to an account as a ListDisks struct
func (d Disks) List(ctx context.Context, req ListRequest) (ListDisks, error) { func (d Disks) List(ctx context.Context, req ListRequest) (*ListDisks, error) {
res, err := d.ListRaw(ctx, req)
if err != nil {
return nil, err
}
list := ListDisks{}
err = json.Unmarshal(res, &list)
if err != nil {
return nil, err
}
return &list, nil
}
// ListRaw gets list of the created disks belonging to an account as an array of bytes
func (d Disks) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) {
url := "/cloudapi/disks/list" url := "/cloudapi/disks/list"
res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil { return res, err
return nil, err
}
list := ListDisks{}
err = json.Unmarshal(res, &list)
if err != nil {
return nil, err
}
return list, nil
}
// ListDeleted gets list the deleted disks belonging to an account
func (d Disks) ListDeleted(ctx context.Context, req ListRequest) (ListDisks, error) {
url := "/cloudapi/disks/listDeleted"
res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil {
return nil, err
}
list := ListDisks{}
err = json.Unmarshal(res, &list)
if err != nil {
return nil, err
}
return list, nil
} }

View File

@@ -0,0 +1,65 @@
package disks
import (
"context"
"encoding/json"
"net/http"
)
// Request struct for get list of deleted disks
type ListDeletedRequest 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 account name
// Required: false
AccountName string `url:"accountName,omitempty" json:"accountName,omitempty"`
// Find by max size disk
// Required: false
DiskMaxSize int64 `url:"diskMaxSize,omitempty" json:"diskMaxSize,omitempty"`
// Find by shared, true or false
// Required: false
Shared bool `url:"shared,omitempty" json:"shared,omitempty"`
// ID of the account the disks belong to
// Required: false
AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"`
// Type of the disks
// Required: false
Type string `url:"type,omitempty" json:"type,omitempty"`
// Page number
// Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"`
// Page size
// Required: false
Size uint64 `url:"size,omitempty" json:"size,omitempty"`
}
// ListDeleted gets list the deleted disks belonging to an account
func (d Disks) ListDeleted(ctx context.Context, req ListDeletedRequest) (*ListDisks, error) {
url := "/cloudapi/disks/listDeleted"
res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil {
return nil, err
}
list := ListDisks{}
err = json.Unmarshal(res, &list)
if err != nil {
return nil, err
}
return &list, nil
}

View File

@@ -9,12 +9,20 @@ import (
// Request struct for get list types of disks // Request struct for get list types of disks
type ListTypesRequest struct { type ListTypesRequest struct {
// Show detailed disk types by seps // Show detailed disk types by seps
// Required: true
Detailed bool `url:"detailed" json:"detailed" validate:"required"`
// Page number
// Required: false // Required: false
Detailed bool `url:"detailed,omitempty" json:"detailed,omitempty"` Page uint64 `url:"page,omitempty" json:"page,omitempty"`
// Page size
// Required: false
Size uint64 `url:"size,omitempty" json:"size,omitempty"`
} }
// ListTypes gets list defined disk types // ListTypes gets list defined disk types
func (d Disks) ListTypes(ctx context.Context, req ListTypesRequest) ([]interface{}, error) { func (d Disks) ListTypes(ctx context.Context, req ListTypesRequest) (*ListTypes, error) {
url := "/cloudapi/disks/listTypes" url := "/cloudapi/disks/listTypes"
res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req)
@@ -22,12 +30,12 @@ func (d Disks) ListTypes(ctx context.Context, req ListTypesRequest) ([]interface
return nil, err return nil, err
} }
list := make([]interface{}, 0) list := ListTypes{}
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,13 +8,49 @@ import (
// Request struct for get list unattached disk // Request struct for get list unattached disk
type ListUnattachedRequest struct { type ListUnattachedRequest struct {
// Find by id
// Required: false
ByID uint64 `url:"by_id,omitempty" json:"by_id,omitempty"`
// Find by account name
// Required: false
AccountName string `url:"accountName,omitempty" json:"accountName,omitempty"`
// Find by max size disk
// Required: false
DiskMaxSize int64 `url:"diskMaxSize,omitempty" json:"diskMaxSize,omitempty"`
// Find by status
// Required: false
Status string `url:"status,omitempty" json:"status,omitempty"`
// Type of the disks
// Required: false
Type string `url:"type,omitempty" json:"type,omitempty"`
// ID of the account // ID of the account
// Required: false // Required: false
AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"`
// Find by sep ID
// Required: false
SEPID uint64 `url:"sepId,omitempty" json:"sepId,omitempty"`
// Find by pool name
// Required: false
Pool string `url:"pool,omitempty" json:"pool,omitempty"`
// Page number
// Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"`
// Page size
// Required: false
Size uint64 `url:"size,omitempty" json:"size,omitempty"`
} }
// ListUnattached gets list of unattached disks // ListUnattached gets list of unattached disks
func (d Disks) ListUnattached(ctx context.Context, req ListUnattachedRequest) (ListDisks, error) { func (d Disks) ListUnattached(ctx context.Context, req ListUnattachedRequest) (*ListDisksUnattached, error) {
url := "/cloudapi/disks/listUnattached" url := "/cloudapi/disks/listUnattached"
res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req)
@@ -22,12 +58,12 @@ func (d Disks) ListUnattached(ctx context.Context, req ListUnattachedRequest) (L
return nil, err return nil, err
} }
list := ListDisks{} list := ListDisksUnattached{}
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

@@ -114,8 +114,158 @@ type ItemDisk struct {
VMID uint64 `json:"vmid"` VMID uint64 `json:"vmid"`
} }
type ItemDiskUnattached struct {
// CKey
CKey string `json:"_ckey"`
// Meta
Meta []interface{} `json:"_meta"`
// Account ID
AccountID uint64 `json:"accountId"`
// Account name
AccountName string `json:"accountName"`
// Access Control List
ACL map[string]interface{} `json:"acl"`
// Boot Partition
BootPartition uint64 `json:"bootPartition"`
// Created time
CreatedTime uint64 `json:"createdTime"`
// Deleted time
DeletedTime uint64 `json:"deletedTime"`
// Description
Description string `json:"desc"`
// Destruction time
DestructionTime uint64 `json:"destructionTime"`
// Disk path
DiskPath string `json:"diskPath"`
// Grid ID
GID uint64 `json:"gid"`
// GUID
GUID uint64 `json:"guid"`
// ID
ID uint64 `json:"id"`
// Image ID
ImageID uint64 `json:"imageId"`
// Images
Images []uint64 `json:"images"`
// IOTune
IOTune IOTune `json:"iotune"`
// IQN
IQN string `json:"iqn"`
// Login
Login string `json:"login"`
// Milestones
Milestones uint64 `json:"milestones"`
// Name
Name string `json:"name"`
// Order
Order uint64 `json:"order"`
// Params
Params string `json:"params"`
// Parent ID
ParentID uint64 `json:"parentId"`
// Password
Password string `json:"passwd"`
//PCISlot
PCISlot int64 `json:"pciSlot"`
// Pool
Pool string `json:"pool"`
// Present to
PresentTo []uint64 `json:"presentTo"`
// Purge attempts
PurgeAttempts uint64 `json:"purgeAttempts"`
// Purge time
PurgeTime uint64 `json:"purgeTime"`
// Reality device number
RealityDeviceNumber uint64 `json:"realityDeviceNumber"`
// Reference ID
ReferenceID string `json:"referenceId"`
// Resource ID
ResID string `json:"resId"`
// Resource name
ResName string `json:"resName"`
// Role
Role string `json:"role"`
// ID SEP
SEPID uint64 `json:"sepId"`
// Shareable
Shareable bool `json:"shareable"`
// Size max
SizeMax uint64 `json:"sizeMax"`
// Size used
SizeUsed float64 `json:"sizeUsed"`
// List of snapshots
Snapshots ListSnapshots `json:"snapshots"`
// Status
Status string `json:"status"`
// Tech status
TechStatus string `json:"techStatus"`
// Type
Type string `json:"type"`
// Virtual machine ID
VMID uint64 `json:"vmid"`
}
// List of disks searched
type ListSearchDisks []ItemDisk
// List of disks // List of disks
type ListDisks []ItemDisk type ListDisks struct {
// Data
Data []ItemDisk `json:"data"`
// Entry count
EntryCount uint64 `json:"entryCount"`
}
// List of unattached disks
type ListDisksUnattached struct {
Data []ItemDiskUnattached `json:"data"`
EntryCount uint64 `json:"entryCount"`
}
// Main information about snapshot // Main information about snapshot
type ItemSnapshot struct { type ItemSnapshot struct {
@@ -125,6 +275,8 @@ type ItemSnapshot struct {
// Label // Label
Label string `json:"label"` Label string `json:"label"`
ReferenceID string `json:"referenceId"`
// Resource ID // Resource ID
ResID string `json:"resId"` ResID string `json:"resId"`
@@ -240,7 +392,7 @@ type RecordDisk struct {
ParentID uint64 `json:"parentId"` ParentID uint64 `json:"parentId"`
// PCI slot // PCI slot
PCISlot uint64 `json:"pciSlot"` PCISlot int64 `json:"pciSlot"`
// Pool // Pool
Pool string `json:"pool"` Pool string `json:"pool"`
@@ -290,3 +442,11 @@ type RecordDisk struct {
// Virtual machine ID // Virtual machine ID
VMID uint64 `json:"vmid"` VMID uint64 `json:"vmid"`
} }
type ListTypes struct {
// Data
Data []interface{} `json:"data"`
// Entry count
EntryCount uint64 `json:"entryCount"`
}

View File

@@ -21,7 +21,7 @@ type SearchRequest struct {
} }
// Search search disks // Search search disks
func (d Disks) Search(ctx context.Context, req SearchRequest) (ListDisks, error) { func (d Disks) Search(ctx context.Context, req SearchRequest) (ListSearchDisks, error) {
url := "/cloudapi/disks/search" url := "/cloudapi/disks/search"
res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req) res, err := d.client.DecortApiCall(ctx, http.MethodPost, url, req)
@@ -29,7 +29,7 @@ func (d Disks) Search(ctx context.Context, req SearchRequest) (ListDisks, error)
return nil, err return nil, err
} }
list := ListDisks{} list := ListSearchDisks{}
err = json.Unmarshal(res, &list) err = json.Unmarshal(res, &list)
if err != nil { if err != nil {

View File

@@ -12,6 +12,26 @@ import (
// - First argument -> prefix // - First argument -> prefix
// - Second argument -> indent // - Second argument -> indent
func (ld ListDisks) Serialize(params ...string) (serialization.Serialized, error) { func (ld ListDisks) Serialize(params ...string) (serialization.Serialized, error) {
if ld.EntryCount == 0 {
return []byte{}, nil
}
if len(params) > 1 {
prefix := params[0]
indent := params[1]
return json.MarshalIndent(ld, prefix, indent)
}
return json.Marshal(ld)
}
// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions.
//
// In order to serialize with indent make sure to follow these guidelines:
// - First argument -> prefix
// - Second argument -> indent
func (ld ListSearchDisks) Serialize(params ...string) (serialization.Serialized, error) {
if len(ld) == 0 { if len(ld) == 0 {
return []byte{}, nil return []byte{}, nil
} }
@@ -41,3 +61,39 @@ func (idisk ItemDisk) Serialize(params ...string) (serialization.Serialized, err
return json.Marshal(idisk) return json.Marshal(idisk)
} }
// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions.
//
// In order to serialize with indent make sure to follow these guidelines:
// - First argument -> prefix
// - Second argument -> indent
func (lu ListDisksUnattached) Serialize(params ...string) (serialization.Serialized, error) {
if lu.EntryCount == 0 {
return []byte{}, nil
}
if len(params) > 1 {
prefix := params[0]
indent := params[1]
return json.MarshalIndent(lu, prefix, indent)
}
return json.Marshal(lu)
}
// Serialize returns JSON-serialized []byte. Used as a wrapper over json.Marshal and json.MarshalIndent functions.
//
// In order to serialize with indent make sure to follow these guidelines:
// - First argument -> prefix
// - Second argument -> indent
func (idisk ItemDiskUnattached) Serialize(params ...string) (serialization.Serialized, error) {
if len(params) > 1 {
prefix := params[0]
indent := params[1]
return json.MarshalIndent(idisk, prefix, indent)
}
return json.Marshal(idisk)
}

View File

@@ -6,6 +6,63 @@ 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 (ld ListDisks) SortByCreatedTime(inverse bool) ListDisks { func (ld ListDisks) SortByCreatedTime(inverse bool) ListDisks {
if len(ld.Data) < 2 {
return ld
}
sort.Slice(ld.Data, func(i, j int) bool {
if inverse {
return ld.Data[i].CreatedTime > ld.Data[j].CreatedTime
}
return ld.Data[i].CreatedTime < ld.Data[j].CreatedTime
})
return ld
}
// SortByDestructionTime sorts ListDisks by the DestructionTime field in ascending order.
//
// If inverse param is set to true, the order is reversed.
func (ld ListDisks) SortByDestructionTime(inverse bool) ListDisks {
if len(ld.Data) < 2 {
return ld
}
sort.Slice(ld.Data, func(i, j int) bool {
if inverse {
return ld.Data[i].DestructionTime > ld.Data[j].DestructionTime
}
return ld.Data[i].DestructionTime < ld.Data[j].DestructionTime
})
return ld
}
// SortByDeletedTime sorts ListDisks by the DeletedTime field in ascending order.
//
// If inverse param is set to true, the order is reversed.
func (ld ListDisks) SortByDeletedTime(inverse bool) ListDisks {
if len(ld.Data) < 2 {
return ld
}
sort.Slice(ld.Data, func(i, j int) bool {
if inverse {
return ld.Data[i].DeletedTime > ld.Data[j].DeletedTime
}
return ld.Data[i].DeletedTime < ld.Data[j].DeletedTime
})
return ld
}
// SortByCreatedTime sorts ListSearchDisks by the CreatedTime field in ascending order.
//
// If inverse param is set to true, the order is reversed.
func (ld ListSearchDisks) SortByCreatedTime(inverse bool) ListSearchDisks {
if len(ld) < 2 { if len(ld) < 2 {
return ld return ld
} }
@@ -21,10 +78,10 @@ func (ld ListDisks) SortByCreatedTime(inverse bool) ListDisks {
return ld return ld
} }
// SortByDestructionTime sorts ListDisks by the DestructionTime field in ascending order. // SortByDestructionTime sorts ListSearchDisks by the DestructionTime field in ascending order.
// //
// If inverse param is set to true, the order is reversed. // If inverse param is set to true, the order is reversed.
func (ld ListDisks) SortByDestructionTime(inverse bool) ListDisks { func (ld ListSearchDisks) SortByDestructionTime(inverse bool) ListSearchDisks {
if len(ld) < 2 { if len(ld) < 2 {
return ld return ld
} }
@@ -40,10 +97,10 @@ func (ld ListDisks) SortByDestructionTime(inverse bool) ListDisks {
return ld return ld
} }
// SortByDeletedTime sorts ListDisks by the DeletedTime field in ascending order. // SortByDeletedTime sorts ListSearchDisks by the DeletedTime field in ascending order.
// //
// If inverse param is set to true, the order is reversed. // If inverse param is set to true, the order is reversed.
func (ld ListDisks) SortByDeletedTime(inverse bool) ListDisks { func (ld ListSearchDisks) SortByDeletedTime(inverse bool) ListSearchDisks {
if len(ld) < 2 { if len(ld) < 2 {
return ld return ld
} }
@@ -58,3 +115,60 @@ func (ld ListDisks) SortByDeletedTime(inverse bool) ListDisks {
return ld return ld
} }
// SortByCreatedTime sorts ListDisksUnattached by the CreatedTime field in ascending order.
//
// If inverse param is set to true, the order is reversed.
func (lu ListDisksUnattached) SortByCreatedTime(inverse bool) ListDisksUnattached {
if len(lu.Data) < 2 {
return lu
}
sort.Slice(lu.Data, func(i, j int) bool {
if inverse {
return lu.Data[i].CreatedTime > lu.Data[j].CreatedTime
}
return lu.Data[i].CreatedTime < lu.Data[j].CreatedTime
})
return lu
}
// SortByDestructionTime sorts ListDisksUnattached by the DestructionTime field in ascending order.
//
// If inverse param is set to true, the order is reversed.
func (lu ListDisksUnattached) SortByDestructionTime(inverse bool) ListDisksUnattached {
if len(lu.Data) < 2 {
return lu
}
sort.Slice(lu.Data, func(i, j int) bool {
if inverse {
return lu.Data[i].DestructionTime > lu.Data[j].DestructionTime
}
return lu.Data[i].DestructionTime < lu.Data[j].DestructionTime
})
return lu
}
// SortByDeletedTime sorts ListDisksUnattached by the DeletedTime field in ascending order.
//
// If inverse param is set to true, the order is reversed.
func (lu ListDisksUnattached) SortByDeletedTime(inverse bool) ListDisksUnattached {
if len(lu.Data) < 2 {
return lu
}
sort.Slice(lu.Data, func(i, j int) bool {
if inverse {
return lu.Data[i].DeletedTime > lu.Data[j].DeletedTime
}
return lu.Data[i].DeletedTime < lu.Data[j].DeletedTime
})
return lu
}

View File

@@ -31,21 +31,23 @@ func (lenet ListExtNets) FilterByStatus(status string) ListExtNets {
func (lenet ListExtNets) FilterFunc(predicate func(ItemExtNet) bool) ListExtNets { func (lenet ListExtNets) FilterFunc(predicate func(ItemExtNet) bool) ListExtNets {
var result ListExtNets var result ListExtNets
for _, item := range lenet { for _, item := range lenet.Data {
if predicate(item) { if predicate(item) {
result = append(result, item) result.Data = append(result.Data, item)
} }
} }
result.EntryCount = uint64(len(result.Data))
return result return result
} }
// FindOne returns first found ItemExtNet // FindOne returns first found ItemExtNet
// If none was found, returns an empty struct. // If none was found, returns an empty struct.
func (lenet ListExtNets) FindOne() ItemExtNet { func (lenet ListExtNets) FindOne() ItemExtNet {
if len(lenet) == 0 { if lenet.EntryCount == 0 {
return ItemExtNet{} return ItemExtNet{}
} }
return lenet[0] return lenet.Data[0]
} }

View File

@@ -3,23 +3,25 @@ package extnet
import "testing" import "testing"
var extnets = ListExtNets{ var extnets = ListExtNets{
ItemExtNet{ Data: []ItemExtNet{
ID: 3, {
IPCIDR: "176.118.164.0/24", ID: 3,
Name: "176.118.164.0/24", IPCIDR: "176.118.164.0/24",
Status: "ENABLED", Name: "176.118.164.0/24",
}, Status: "ENABLED",
ItemExtNet{ },
ID: 10, {
IPCIDR: "45.134.255.0/24", ID: 10,
Name: "45.134.255.0/24", IPCIDR: "45.134.255.0/24",
Status: "ENABLED", Name: "45.134.255.0/24",
}, Status: "ENABLED",
ItemExtNet{ },
ID: 13, {
IPCIDR: "88.218.249.0/24", ID: 13,
Name: "88.218.249.0/24", IPCIDR: "88.218.249.0/24",
Status: "DISABLED", Name: "88.218.249.0/24",
Status: "DISABLED",
},
}, },
} }
@@ -43,11 +45,11 @@ func TestFilterByName(t *testing.T) {
func TestFilterByStatus(t *testing.T) { func TestFilterByStatus(t *testing.T) {
actual := extnets.FilterByStatus("ENABLED") actual := extnets.FilterByStatus("ENABLED")
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 != "ENABLED" { if item.Status != "ENABLED" {
t.Fatal("expected Status 'ENABLED', found: ", item.Status) t.Fatal("expected Status 'ENABLED', found: ", item.Status)
} }
@@ -59,7 +61,7 @@ func TestFilterFunc(t *testing.T) {
return ien.IPCIDR == ien.Name return ien.IPCIDR == ien.Name
}) })
if len(actual) != 3 { if len(actual.Data) != 3 {
t.Fatal("expected 3 elements, found: ", len(actual)) t.Fatal("expected 3 elements, found: ", 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 detailed information about external network // GetRequest struct to get detailed information about external network
type GetRequest struct { type GetRequest struct {
// ID of external network // ID of external network
// Required: true // Required: true
NetID uint64 `url:"net_id" json:"net_id" validate:"required"` NetID uint64 `url:"net_id" json:"net_id" validate:"required"`
} }
// Get gets detailed information about external network // Get gets detailed information about external network as a RecordExtNet struct
func (e ExtNet) Get(ctx context.Context, req GetRequest) (*RecordExtNet, error) { func (e ExtNet) Get(ctx context.Context, req GetRequest) (*RecordExtNet, error) {
err := validators.ValidateRequest(req) res, err := e.GetRaw(ctx, req)
if err != nil {
for _, validationError := range validators.GetErrors(err) {
return nil, validators.ValidationError(validationError)
}
}
url := "/cloudapi/extnet/get"
res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -40,3 +31,18 @@ func (e ExtNet) Get(ctx context.Context, req GetRequest) (*RecordExtNet, error)
return &info, nil return &info, nil
} }
// GetRaw gets detailed information about external network as an array of bytes
func (e ExtNet) GetRaw(ctx context.Context, req GetRequest) ([]byte, error) {
err := validators.ValidateRequest(req)
if err != nil {
for _, validationError := range validators.GetErrors(err) {
return nil, validators.ValidationError(validationError)
}
}
url := "/cloudapi/extnet/get"
res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req)
return res, err
}

View File

@@ -6,12 +6,36 @@ import (
"net/http" "net/http"
) )
// Request struct for get list external network // ListRequest struct to get list of external network
type ListRequest struct { type ListRequest struct {
// Filter by account ID // Find by account ID
// Required: false // Required: false
AccountID uint64 `url:"accountId,omitempty" json:"accountId,omitempty"` AccountID uint64 `url:"accountId,omitempty" json:"accountId,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 network ip address
// Required: false
Network string `url:"network,omitempty" json:"network,omitempty"`
// Find by vlan ID
// Required: false
VLANID uint64 `url:"vlanId,omitempty" json:"vlanId,omitempty"`
// Find by vnfDevices ID
// Required: false
VNFDevID uint64 `url:"vnfDevId,omitempty" json:"vnfDevId,omitempty"`
// Find by status
// Required: false
Status string `url:"status,omitempty" json:"status,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"`
@@ -21,11 +45,9 @@ type ListRequest struct {
Size uint64 `url:"size,omitempty" json:"size,omitempty"` Size uint64 `url:"size,omitempty" json:"size,omitempty"`
} }
// List gets list all available external networks // List gets list of all available external networks as a ListExtNets struct
func (e ExtNet) List(ctx context.Context, req ListRequest) (ListExtNets, error) { func (e ExtNet) List(ctx context.Context, req ListRequest) (*ListExtNets, error) {
url := "/cloudapi/extnet/list" res, err := e.ListRaw(ctx, req)
res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -37,5 +59,13 @@ func (e ExtNet) List(ctx context.Context, req ListRequest) (ListExtNets, error)
return nil, err return nil, err
} }
return list, nil return &list, nil
}
// ListRaw gets list of all available external networks as an array of bytes
func (e ExtNet) ListRaw(ctx context.Context, req ListRequest) ([]byte, error) {
url := "/cloudapi/extnet/list"
res, err := e.client.DecortApiCall(ctx, http.MethodPost, url, req)
return res, err
} }

View File

@@ -13,10 +13,26 @@ type ListComputesRequest struct {
// Filter by account ID // Filter by account ID
// Required: true // Required: true
AccountID uint64 `url:"accountId" json:"accountId" validate:"required"` AccountID uint64 `url:"accountId" json:"accountId" validate:"required"`
// Find by rg ID
// Required: false
RGID uint64 `url:"rgId,omitempty" json:"rgId,omitempty"`
// Find by compute ID
// Required: false
ComputeID uint64 `url:"computeId,omitempty" json:"computeId,omitempty"`
// Page number
// Required: false
Page uint64 `url:"page,omitempty" json:"page,omitempty"`
// Page size
// Required: false
Size uint64 `url:"size,omitempty" json:"size,omitempty"`
} }
// ListComputes gets computes from account with extnets // ListComputes gets computes from account with extnets
func (e ExtNet) ListComputes(ctx context.Context, req ListComputesRequest) (ListExtNetComputes, error) { func (e ExtNet) ListComputes(ctx context.Context, req ListComputesRequest) (*ListExtNetComputes, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { for _, validationError := range validators.GetErrors(err) {
@@ -38,5 +54,5 @@ func (e ExtNet) ListComputes(ctx context.Context, req ListComputesRequest) (List
return nil, err return nil, err
} }
return list, nil return &list, nil
} }

View File

@@ -25,7 +25,11 @@ type ItemExtNetExtend struct {
} }
// List of information about external network // List of information about external network
type ListExtNets []ItemExtNet type ListExtNets struct {
Data []ItemExtNet `json:"data"`
EntryCount uint64 `json:"entryCount"`
}
// List of extend information about external network // List of extend information about external network
type ListExtNetExtends []ItemExtNetExtend type ListExtNetExtends []ItemExtNetExtend
@@ -55,10 +59,19 @@ type ItemExtNetCompute struct {
} }
// List of information about computes with external network // List of information about computes with external network
type ListExtNetComputes []ItemExtNetCompute type ListExtNetComputes struct {
// Data
Data []ItemExtNetCompute `json:"data"`
// Entry count
EntryCount uint64 `json:"entryCount"`
}
// QOS // QOS
type QOS struct { type QOS struct {
// EBurst
EBurst uint64 `json:"eBurst"`
// ERate // ERate
ERate uint64 `json:"eRate"` ERate uint64 `json:"eRate"`
@@ -107,6 +120,29 @@ type VNFs struct {
DHCP uint64 `json:"dhcp"` DHCP uint64 `json:"dhcp"`
} }
type Excluded struct {
// ClientType
ClientType string `json:"clientType"`
// Domain name
DomainName string `json:"domainname"`
// Host name
HostName string `json:"hostname"`
// IP
IP string `json:"ip"`
// MAC
MAC string `json:"mac"`
// Type
Type string `json:"type"`
// VMID
VMID uint64 `json:"vmId"`
}
// Detailed information about external network // Detailed information about external network
type RecordExtNet struct { type RecordExtNet struct {
// CKey // CKey
@@ -115,11 +151,8 @@ type RecordExtNet struct {
// Meta // Meta
Meta []interface{} `json:"_meta"` Meta []interface{} `json:"_meta"`
// CheckIPs
CheckIPs []string `json:"checkIPs"`
// CheckIps // CheckIps
CheckIps []string `json:"checkIps"` CheckIPs []string `json:"checkIps"`
// Default // Default
Default bool `json:"default"` Default bool `json:"default"`
@@ -134,7 +167,7 @@ type RecordExtNet struct {
DNS []string `json:"dns"` DNS []string `json:"dns"`
// Excluded // Excluded
Excluded []string `json:"excluded"` Excluded []Excluded `json:"excluded"`
// Free IPs // Free IPs
FreeIPs uint64 `json:"free_ips"` FreeIPs uint64 `json:"free_ips"`

View File

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

View File

@@ -45,7 +45,7 @@ type CreateRequest struct {
} }
// Create method will create a new FLIPGorup in the specified Account // Create method will create a new FLIPGorup in the specified Account
func (f FLIPGroup) Create(ctx context.Context, req CreateRequest) (*RecordFLIPGroup, error) { func (f FLIPGroup) Create(ctx context.Context, req CreateRequest) (*RecordFLIPGroupCreated, error) {
err := validators.ValidateRequest(req) err := validators.ValidateRequest(req)
if err != nil { if err != nil {
for _, validationError := range validators.GetErrors(err) { for _, validationError := range validators.GetErrors(err) {
@@ -60,7 +60,7 @@ func (f FLIPGroup) Create(ctx context.Context, req CreateRequest) (*RecordFLIPGr
return nil, err return nil, err
} }
info := RecordFLIPGroup{} info := RecordFLIPGroupCreated{}
err = json.Unmarshal(res, &info) err = json.Unmarshal(res, &info)
if err != nil { if err != nil {

View File

@@ -1,96 +0,0 @@
package flipgroup
// FilterByID returns ListFLIPGroups with specified ID.
func (lfg ListFLIPGroups) FilterByID(id uint64) ListFLIPGroups {
predicate := func(ifg ItemFLIPGroup) bool {
return ifg.ID == id
}
return lfg.FilterFunc(predicate)
}
// FilterByAccountID returns ListFLIPGroups with specified AccountID.
func (lfg ListFLIPGroups) FilterByAccountID(accountID uint64) ListFLIPGroups {
predicate := func(ifg ItemFLIPGroup) bool {
return ifg.AccountID == accountID
}
return lfg.FilterFunc(predicate)
}
// FilterByRGID returns ListFLIPGroups with specified RGID.
func (lfg ListFLIPGroups) FilterByRGID(rgID uint64) ListFLIPGroups {
predicate := func(ifg ItemFLIPGroup) bool {
return ifg.RGID == rgID
}
return lfg.FilterFunc(predicate)
}
// FilterByName returns ListFLIPGroups with specified Name.
func (lfg ListFLIPGroups) FilterByName(name string) ListFLIPGroups {
predicate := func(ifg ItemFLIPGroup) bool {
return ifg.Name == name
}
return lfg.FilterFunc(predicate)
}
// FilterByStatus returns ListFLIPGroups with specified Status.
func (lfg ListFLIPGroups) FilterByStatus(status string) ListFLIPGroups {
predicate := func(ifg ItemFLIPGroup) bool {
return ifg.Status == status
}
return lfg.FilterFunc(predicate)
}
// FilterByCreatedBy returns ListFLIPGroups created by specified user.
func (lfg ListFLIPGroups) FilterByCreatedBy(createdBy string) ListFLIPGroups {
predicate := func(ifg ItemFLIPGroup) bool {
return ifg.CreatedBy == createdBy
}
return lfg.FilterFunc(predicate)
}
// FilterByUpdatedBy returns ListFLIPGroups updated by specified user.
func (lfg ListFLIPGroups) FilterByUpdatedBy(updatedBy string) ListFLIPGroups {
predicate := func(ifg ItemFLIPGroup) bool {
return ifg.UpdatedBy == updatedBy
}
return lfg.FilterFunc(predicate)
}
// FilterByDeletedBy returns ListFLIPGroups deleted by specified user.
func (lfg ListFLIPGroups) FilterByDeletedBy(deletedBy string) ListFLIPGroups {
predicate := func(ifg ItemFLIPGroup) bool {
return ifg.DeletedBy == deletedBy
}
return lfg.FilterFunc(predicate)
}
// FilterFunc allows filtering ListFLIPGroups based on a user-specified predicate.
func (lfg ListFLIPGroups) FilterFunc(predicate func(ItemFLIPGroup) bool) ListFLIPGroups {
var result ListFLIPGroups
for _, item := range lfg {
if predicate(item) {
result = append(result, item)
}
}
return result
}
// FindOne returns first found ItemFLIPGroup
// If none was found, returns an empty struct.
func (lfg ListFLIPGroups) FindOne() ItemFLIPGroup {
if len(lfg) == 0 {
return ItemFLIPGroup{}
}
return lfg[0]
}

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