Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 96273d2a12 | |||
| d4065938ac |
29
CHANGELOG.md
29
CHANGELOG.md
@@ -1,30 +1,5 @@
|
||||
## Version 1.7.2
|
||||
## Version 1.7.4
|
||||
|
||||
## Bugfix
|
||||
- Fix panic in clients
|
||||
- Add refresh Token in BVS authorization mode and refactoring bvs_client
|
||||
- Delete tag validate and omitempty in Permanently field, DeleteRequest model in cloudApi/k8s/delete cloudBroker/k8s/delete
|
||||
- Add Page and Size fields into model AffinityGroupsListRequest in cloudbroker/rg/affinity_groups_list;
|
||||
- Change type of Data field from for model ListAffinityGroup in cloudbroker/rg/affinity_groups_list for correct parsing;
|
||||
- Add new models ListAffinityGroupItems and ItemAffinityGroup to support correct parsing for model ListAffinityGroup in cloudbroker/rg/affinity_groups_list;
|
||||
- Add missing IDs() method for model ListAffinityGroupItems in cloudbroker/rg/affinity_groups_list;
|
||||
- Add ResourceLimits field into model ItemResourceConsumption in cloudbroker/rg/models;
|
||||
- Add LockStatus field into model ListRequest in cloudbroker/rg/list;
|
||||
- Fix url for cloudbroker/grid/getDiagnosisGet
|
||||
- Create ListUsers model with Data and EntryCount fields in cloudbroker/compute/models;
|
||||
- Change type of return structure for UserList method from RecordACL to ListUsers in cloudbroker/compute/user_list;
|
||||
- Create ListSnapShot model with Data and EntryCount fields in cloudbroker/compute/models;
|
||||
- Change type of return structure for SnapshotList from ListSnapshots to *ListSnapShot in cloudbroker/compute/snapshot_list;
|
||||
- Change json tag for TotalDiskSize field for model ItemCompute in cloudbroker/compute/models;
|
||||
- Add NeedReboot and CdImageId fields for model InfoCompute in cloudbroker/compute/models;
|
||||
- Reorganize ListPFW model by adding Data and EntryCount fields with json tags in cloudbroker/compute/models;
|
||||
- Fix IDs() method for ListPFW model in cloudbroker/compute/ids;
|
||||
- Change type of return structure for PFWList method from ListPFW to *ListPFW in cloudbroker/compute/pfw_list.
|
||||
|
||||
|
||||
## Feature
|
||||
- Add endpoints AccessAdd AccessRemote in cloudBroker/k8ci
|
||||
- Add endpoints Audit in cloudBroker
|
||||
- Add AuthBroker field to model RecordGrid in cloudbroker/grid/models
|
||||
- Add AuthBroker field to model ItemGridList in cloudbroker/grid/models
|
||||
- Add endpoints DeleteCustomFields in cloudbroker/compute
|
||||
- Remove tag "required" from field Value in AffinityRuleAddRequest, AntiAffinityRuleAddRequest, AffinityRuleRemoveRequest AntiAffinityRuleRemoveRequest in cloudapi/compute and cloudbroker/compute
|
||||
|
||||
274
README.md
274
README.md
@@ -17,29 +17,44 @@ Decort SDK - это библиотека, написанная на языке G
|
||||
|
||||
- [Установка](#установка)
|
||||
- [Список API](#список-api)
|
||||
- [Cloudapi](#cloudapi)
|
||||
- [Cloudbroker](#cloudbroker)
|
||||
- [Cloudapi](#cloudapi)
|
||||
- [Cloudbroker](#cloudbroker)
|
||||
- [Работа с библиотекой](#работа-с-библиотекой)
|
||||
- [Настройка конфигурации клиента](#настройка-конфигурации-клиента)
|
||||
- [Пример конфигурации клиента](#пример-конфигурации-клиента)
|
||||
- [Парсинг конфигурации из файла](#парсинг-конфигурации-из-файла)
|
||||
- [Пример JSON конфигурации](#пример-json-конфигурации)
|
||||
- [Пример YAML конфигурации](#пример-yaml-конфигурации)
|
||||
- [Создание клиента](#создание-клиента)
|
||||
- [Создание структуры запроса](#cоздание-структуры-запроса)
|
||||
- [Выполнение запроса](#выполнение-запроса)
|
||||
- [Фильтрация](#фильтрация)
|
||||
- [Сортировка](#сортировка)
|
||||
- [Сериализация](#сериализация)
|
||||
- [Настройка конфигурации клиента](#настройка-конфигурации-клиента)
|
||||
- [Пример конфигурации клиента](#пример-конфигурации-клиента)
|
||||
- [Парсинг конфигурации из файла](#парсинг-конфигурации-из-файла)
|
||||
- [Пример JSON конфигурации](#пример-json-конфигурации)
|
||||
- [Пример YAML конфигурации](#пример-yaml-конфигурации)
|
||||
- [Создание клиента](#создание-клиента)
|
||||
- [Создание структуры запроса](#cоздание-структуры-запроса)
|
||||
- [Выполнение запроса](#выполнение-запроса)
|
||||
- [Фильтрация](#фильтрация)
|
||||
- [Сортировка](#сортировка)
|
||||
- [Сериализация](#сериализация)
|
||||
- [Получение списка уникальных идентификаторов (ID) объекта](#получение-списка-уникальных-идентификаторов-id-объекта)
|
||||
- [Работа с legacy клиентом](#работа-с-legacy-клиентом)
|
||||
- [Настройка конфигурации legacy клиента](#настройка-конфигурации-legacy-клиента)
|
||||
- [Пример конфигурации legacy клиента](#пример-конфигурации-legacy-клиента)
|
||||
- [Парсинг legacy конфигурации из файла](#парсинг-legacy-конфигурации-из-файла)
|
||||
- [Пример legacy JSON конфигурации](#пример-legacy-json-конфигурации)
|
||||
- [Пример legacy YAML конфигурации](#пример-legacy-yaml-конфигурации)
|
||||
- [Создание legacy клиента](#создание-legacy-клиента)
|
||||
- [Создание структуры запроса](#cоздание-структуры-запроса)
|
||||
- [Выполнение запроса](#выполнение-запроса)
|
||||
- [Настройка конфигурации legacy клиента](#настройка-конфигурации-legacy-клиента)
|
||||
- [Пример конфигурации legacy клиента](#пример-конфигурации-legacy-клиента)
|
||||
- [Парсинг legacy конфигурации из файла](#парсинг-legacy-конфигурации-из-файла)
|
||||
- [Пример legacy JSON конфигурации](#пример-legacy-json-конфигурации)
|
||||
- [Пример legacy YAML конфигурации](#пример-legacy-yaml-конфигурации)
|
||||
- [Создание legacy клиента](#создание-legacy-клиента)
|
||||
- [Создание структуры запроса](#cоздание-структуры-запроса)
|
||||
- [Выполнение запроса](#выполнение-запроса)
|
||||
- [Работа с BVS клиентом](#работа-с-bvs-клиентом)
|
||||
- [Настройка параметров BVS в кабинете администратора](#настройка-параметров-bvs-в-кабинете-администратора)
|
||||
- [Настройка конфигурации BVS клиента](#настройка-конфигурации-bvs-клиента)
|
||||
- [Описание структуры token](#описание-структуры-token)
|
||||
- [Пример конфигурации BVS клиента](#пример-конфигурации-bvs-клиента)
|
||||
- [Парсинг BVS конфигурации из файла](#парсинг-bvs-конфигурации-из-файла)
|
||||
- [Парсинг BVS токена из файла](#парсинг-bvs-токена-из-файла)
|
||||
- [Пример BVS JSON конфигурации](#пример-bvs-json-конфигурации)
|
||||
- [Пример BVS YAML конфигурации](#пример-bvs-yaml-конфигурации)
|
||||
- [Создание BVS клиента](#создание-bvs-клиента)
|
||||
- [Пример создания BVS клиента](#пример-создания-bvs-клиента)
|
||||
- [Пример получения BVS токена](#пример-получения-bvs-токена)
|
||||
- [Пример обновления BVS токена](#пример-обновления-bvs-токена)
|
||||
- [Пример выполнения запроса](#пример-выполнения-запроса)
|
||||
|
||||
## Установка
|
||||
|
||||
@@ -114,8 +129,8 @@ go get -u repository.basistech.ru/BASIS/decort-golang-sdk
|
||||
Алгоритм работы с библиотекой выглядит следующим образом:
|
||||
|
||||
1. Выполнение одного из действий:
|
||||
- настройка конфигурации клиента;
|
||||
- парсинг конфигурации из файла.
|
||||
- настройка конфигурации клиента;
|
||||
- парсинг конфигурации из файла.
|
||||
2. Создание клиента.
|
||||
3. Создание структуры запроса.
|
||||
4. Выполнение запроса.
|
||||
@@ -157,7 +172,7 @@ func main(){
|
||||
|
||||
#### Парсинг конфигурации из файла
|
||||
|
||||
Также возможно создать переменную конфигурации из JSON или YAML файла, используя функцию `ParseConfigJSON` (или `ParseConfigYAML`) из пакета config.
|
||||
Также возможно создать переменную конфигурации из JSON или YAML файла, используя функцию `ParseConfigJSON` (или `ParseConfigYAML`) из пакета config.
|
||||
<br>
|
||||
*См. пример файлов конфигурации ниже и в директории `samples/`.*
|
||||
|
||||
@@ -239,48 +254,48 @@ func main() {
|
||||
В каждом пакете находятся пакеты групп API:
|
||||
|
||||
- **cloudapi**:
|
||||
- `pkg/cloudapi/account` - для `Account`
|
||||
- `pkg/cloudapi/bservice` - для `Basic Service`
|
||||
- `pkg/cloudapi/compute` - для `Compute`
|
||||
- `pkg/cloudapi/disks` - для `Disks`
|
||||
- `pkg/cloudapi/extnet` - для `ExtNet`
|
||||
- `pkg/cloudapi/flipgroup` - для `FLIPGroup`
|
||||
- `pkg/cloudapi/image` - для `Image`
|
||||
- `pkg/cloudapi/k8ci` - для `K8CI`
|
||||
- `pkg/cloudapi/k8s` - для `K8S`
|
||||
- `pkg/cloudapi/kvmppc` - для `KVMPPC`
|
||||
- `pkg/cloudapi/kvmx86` - для `KVMX86`
|
||||
- `pkg/cloudapi/lb` - для `LB`
|
||||
- `pkg/cloudapi/locations` - для `Locations`
|
||||
- `pkg/cloudapi/rg` - для `RG`
|
||||
- `pkg/cloudapi/sizes` - для `Sizes`
|
||||
- `pkg/cloudapi/stack` - для `Stack`
|
||||
- `pkg/cloudapi/tasks` - для `Tasks`
|
||||
- `pkg/cloudapi/vins` - для `VINS`
|
||||
- `pkg/cloudapi/account` - для `Account`
|
||||
- `pkg/cloudapi/bservice` - для `Basic Service`
|
||||
- `pkg/cloudapi/compute` - для `Compute`
|
||||
- `pkg/cloudapi/disks` - для `Disks`
|
||||
- `pkg/cloudapi/extnet` - для `ExtNet`
|
||||
- `pkg/cloudapi/flipgroup` - для `FLIPGroup`
|
||||
- `pkg/cloudapi/image` - для `Image`
|
||||
- `pkg/cloudapi/k8ci` - для `K8CI`
|
||||
- `pkg/cloudapi/k8s` - для `K8S`
|
||||
- `pkg/cloudapi/kvmppc` - для `KVMPPC`
|
||||
- `pkg/cloudapi/kvmx86` - для `KVMX86`
|
||||
- `pkg/cloudapi/lb` - для `LB`
|
||||
- `pkg/cloudapi/locations` - для `Locations`
|
||||
- `pkg/cloudapi/rg` - для `RG`
|
||||
- `pkg/cloudapi/sizes` - для `Sizes`
|
||||
- `pkg/cloudapi/stack` - для `Stack`
|
||||
- `pkg/cloudapi/tasks` - для `Tasks`
|
||||
- `pkg/cloudapi/vins` - для `VINS`
|
||||
- **cloudbroker**:
|
||||
- `pkg/cloudbroker/account` - для `Account`
|
||||
- `pkg/cloudbroker/apiaccess` - для `APIAccess`
|
||||
- `pkg/cloudbroker/backup` - для `Backup`
|
||||
- `pkg/cloudbroker/compute` - для `Compute`
|
||||
- `pkg/cloudbroker/disks` - для `Disks`
|
||||
- `pkg/cloudbroker/extnet` - для `ExtNet`
|
||||
- `pkg/cloudbroker/flipgroup` - для `FLIPGroup`
|
||||
- `pkg/cloudbroker/grid` - для `Grid`
|
||||
- `pkg/cloudbroker/group` - для `Group`
|
||||
- `pkg/cloudbroker/image` - для `Image`
|
||||
- `pkg/cloudbroker/k8ci` - для `K8CI`
|
||||
- `pkg/cloudbroker/k8s` - для `K8S`
|
||||
- `pkg/cloudbroker/kvmppc` - для `KVMPPC`
|
||||
- `pkg/cloudbroker/kvmx86` - для `KVMX86`
|
||||
- `pkg/cloudbroker/lb` - для `LB`
|
||||
- `pkg/cloudbroker/pcidevice` - для `PCIDevice`
|
||||
- `pkg/cloudbroker/rg` - для `RG`
|
||||
- `pkg/cloudbroker/sep` - для `SEP`
|
||||
- `pkg/cloudbroker/stack` - для `Stack`
|
||||
- `pkg/cloudbroker/tasks` - для `Tasks`
|
||||
- `pkg/cloudbroker/user` - для `User`
|
||||
- `pkg/cloudbroker/vgpu` - для `VGPU`
|
||||
- `pkg/cloudbroker/vins` - для `VINS`
|
||||
- `pkg/cloudbroker/account` - для `Account`
|
||||
- `pkg/cloudbroker/apiaccess` - для `APIAccess`
|
||||
- `pkg/cloudbroker/backup` - для `Backup`
|
||||
- `pkg/cloudbroker/compute` - для `Compute`
|
||||
- `pkg/cloudbroker/disks` - для `Disks`
|
||||
- `pkg/cloudbroker/extnet` - для `ExtNet`
|
||||
- `pkg/cloudbroker/flipgroup` - для `FLIPGroup`
|
||||
- `pkg/cloudbroker/grid` - для `Grid`
|
||||
- `pkg/cloudbroker/group` - для `Group`
|
||||
- `pkg/cloudbroker/image` - для `Image`
|
||||
- `pkg/cloudbroker/k8ci` - для `K8CI`
|
||||
- `pkg/cloudbroker/k8s` - для `K8S`
|
||||
- `pkg/cloudbroker/kvmppc` - для `KVMPPC`
|
||||
- `pkg/cloudbroker/kvmx86` - для `KVMX86`
|
||||
- `pkg/cloudbroker/lb` - для `LB`
|
||||
- `pkg/cloudbroker/pcidevice` - для `PCIDevice`
|
||||
- `pkg/cloudbroker/rg` - для `RG`
|
||||
- `pkg/cloudbroker/sep` - для `SEP`
|
||||
- `pkg/cloudbroker/stack` - для `Stack`
|
||||
- `pkg/cloudbroker/tasks` - для `Tasks`
|
||||
- `pkg/cloudbroker/user` - для `User`
|
||||
- `pkg/cloudbroker/vgpu` - для `VGPU`
|
||||
- `pkg/cloudbroker/vins` - для `VINS`
|
||||
|
||||
Все поля структуры имеют описание, в которых содержится:
|
||||
|
||||
@@ -421,50 +436,50 @@ func main() {
|
||||
|
||||
Доступные методы для `.CloudAPI()`:
|
||||
|
||||
- `.Account()` - для работы с `Account`
|
||||
- `.BService()` - для работы с `BService`
|
||||
- `.Compute()` - для работы с `Compute`
|
||||
- `.Disks()` - для работы с `Disks`
|
||||
- `.ExtNet()` - для работы с `ExtNet`
|
||||
- `.FLIPgroup()` - для работы с `FLIPGroup`
|
||||
- `.Image()` - для работы с `Image`
|
||||
- `.K8CI()` - для работы с `K8CI`
|
||||
- `.K8S()` - для работы с `K8S`
|
||||
- `.KVMPPC()` - для работы с `KVMPPC`
|
||||
- `.KVMx86()` - для работы с `KVMX86`
|
||||
- `.LB()` - для работы с `LB`
|
||||
- `.Locations()` - для работы с `Locations`
|
||||
- `.RG()` - для работы с `RG`
|
||||
- `.Sizes()` - для работы с `Sizes`
|
||||
- `.Stack()` - для работы с `Stack`
|
||||
- `.Tasks()` - для работы с `Tasks`
|
||||
- `.VINS()` - для работы с `VINS`
|
||||
- `.Account()` - для работы с `Account`
|
||||
- `.BService()` - для работы с `BService`
|
||||
- `.Compute()` - для работы с `Compute`
|
||||
- `.Disks()` - для работы с `Disks`
|
||||
- `.ExtNet()` - для работы с `ExtNet`
|
||||
- `.FLIPgroup()` - для работы с `FLIPGroup`
|
||||
- `.Image()` - для работы с `Image`
|
||||
- `.K8CI()` - для работы с `K8CI`
|
||||
- `.K8S()` - для работы с `K8S`
|
||||
- `.KVMPPC()` - для работы с `KVMPPC`
|
||||
- `.KVMx86()` - для работы с `KVMX86`
|
||||
- `.LB()` - для работы с `LB`
|
||||
- `.Locations()` - для работы с `Locations`
|
||||
- `.RG()` - для работы с `RG`
|
||||
- `.Sizes()` - для работы с `Sizes`
|
||||
- `.Stack()` - для работы с `Stack`
|
||||
- `.Tasks()` - для работы с `Tasks`
|
||||
- `.VINS()` - для работы с `VINS`
|
||||
|
||||
Доступные методы для `.CloudBroker()`:
|
||||
|
||||
- `.Account()` - для работы с `Account`
|
||||
- `.APIAccess()` - для работы с `APIAccess`
|
||||
- `.Backup()` - для работы с `Backup`
|
||||
- `.Compute()` - для работы с `Compute`
|
||||
- `.Disks()` - для работы с `Disks`
|
||||
- `.ExtNet()` - для работы с `ExtNet`
|
||||
- `.FLIPGroup()` - для работы с `FLIPGroup`
|
||||
- `.Grid()` - для работы с `Grid`
|
||||
- `.Group()` - для работы с `Group`
|
||||
- `.Image()` - для работы с `Image`
|
||||
- `.K8CI()` - для работы с `K8CI`
|
||||
- `.K8S()` - для работы с `K8S`
|
||||
- `.KVMPPC()` - для работы с `KVMPPC`
|
||||
- `.KVMx86()` - для работы с `KVMX86`
|
||||
- `.LB()` - для работы с `LB`
|
||||
- `.PCIDevice()` - для работы с `PCIDevice`
|
||||
- `.RG()` - для работы с `RG`
|
||||
- `.SEP()` - для работы с `SEP`
|
||||
- `.Stack()` - для работы с `Stack`
|
||||
- `.Tasks()` - для работы с `Tasks`
|
||||
- `.User()` - для работы с `User`
|
||||
- `.VGPU()` - для работы с `VGPU`
|
||||
- `.VINS()` - для работы с `VINS`
|
||||
- `.Account()` - для работы с `Account`
|
||||
- `.APIAccess()` - для работы с `APIAccess`
|
||||
- `.Backup()` - для работы с `Backup`
|
||||
- `.Compute()` - для работы с `Compute`
|
||||
- `.Disks()` - для работы с `Disks`
|
||||
- `.ExtNet()` - для работы с `ExtNet`
|
||||
- `.FLIPGroup()` - для работы с `FLIPGroup`
|
||||
- `.Grid()` - для работы с `Grid`
|
||||
- `.Group()` - для работы с `Group`
|
||||
- `.Image()` - для работы с `Image`
|
||||
- `.K8CI()` - для работы с `K8CI`
|
||||
- `.K8S()` - для работы с `K8S`
|
||||
- `.KVMPPC()` - для работы с `KVMPPC`
|
||||
- `.KVMx86()` - для работы с `KVMX86`
|
||||
- `.LB()` - для работы с `LB`
|
||||
- `.PCIDevice()` - для работы с `PCIDevice`
|
||||
- `.RG()` - для работы с `RG`
|
||||
- `.SEP()` - для работы с `SEP`
|
||||
- `.Stack()` - для работы с `Stack`
|
||||
- `.Tasks()` - для работы с `Tasks`
|
||||
- `.User()` - для работы с `User`
|
||||
- `.VGPU()` - для работы с `VGPU`
|
||||
- `.VINS()` - для работы с `VINS`
|
||||
|
||||
3. Вызвать метод, отвечающий за выполнение запроса и передать в него:
|
||||
|
||||
@@ -767,7 +782,7 @@ func main() {
|
||||
|
||||
```
|
||||
|
||||
### Получение списка уникальных идентификаторов (ID) объекта
|
||||
### Получение списка уникальных идентификаторов ID объекта
|
||||
|
||||
Для всех структур, имеющих поля со списками объектов с уникальными числовыми идентификаторами (ID), добавлены методы IDs(), возвращающие массивы уникальных идентификаторов объектов в этих списках.
|
||||
|
||||
@@ -916,7 +931,7 @@ func main() {
|
||||
legacyCfg.SetTimeout(5 * time.Minute)
|
||||
|
||||
// Создание клиента
|
||||
legacyClient := decort.NewLegacy(cfg)
|
||||
legacyClient := decort.NewLegacy(legacyCfg)
|
||||
}
|
||||
```
|
||||
|
||||
@@ -943,7 +958,7 @@ func main() {
|
||||
}
|
||||
|
||||
// Создание клиента
|
||||
legacyClient := decort.NewLegacy(cfg)
|
||||
legacyClient := decort.NewLegacy(legacyCfg)
|
||||
|
||||
// Создание структуры запроса
|
||||
// CreateRequest - реквест на создание виртуальной машины
|
||||
@@ -956,7 +971,7 @@ func main() {
|
||||
}
|
||||
|
||||
// Выполнение запроса
|
||||
res, err := client.CloudAPI().KVMX86().Create(context.Background(), req)
|
||||
res, err := legacyClient.CloudAPI().KVMX86().Create(context.Background(), req)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -1021,9 +1036,9 @@ func main(){
|
||||
AppSecret: "<APP_SECRET>",
|
||||
Username: "<USERNAME>",
|
||||
Password: "<PASSWORD>",
|
||||
SSOURL: "https://bvs-delta.qa.loc:8443"
|
||||
SSOURL: "https://bvs-delta.qa.loc:8443",
|
||||
DecortURL: "https://delta.qa.loc",
|
||||
Domain: "dynamix"
|
||||
Domain: "dynamix",
|
||||
Retries: 5,
|
||||
}
|
||||
|
||||
@@ -1133,7 +1148,7 @@ func main() {
|
||||
Password: "<PASSWORD>",
|
||||
AppID: "<APPID>",
|
||||
AppSecret: "<APPSECRET>",
|
||||
SSOURL: "https://bvs-delta.qa.loc:8443"
|
||||
SSOURL: "https://bvs-delta.qa.loc:8443",
|
||||
DecortURL: "https://mr4.digitalenergy.online",
|
||||
Domain: "dynamix",
|
||||
Retries: 5,
|
||||
@@ -1142,11 +1157,11 @@ func main() {
|
||||
BVSCfg.SetTimeout(5 * time.Minute)
|
||||
|
||||
// Создание клиента
|
||||
BVSClient := decort.NewBVS(cfg)
|
||||
BVSClient := decort.NewBVS(BVSCfg)
|
||||
}
|
||||
```
|
||||
|
||||
#### Пример получения BVS токена
|
||||
#### Пример получения BVS токена
|
||||
|
||||
В случае указания значения в переменной конфигурации `PathCfg` токен и конфигурация будут записаны в файл в формате `json`, переменная `PathToken` служит для записи токена в файл в формате `json`
|
||||
|
||||
@@ -1162,12 +1177,12 @@ import (
|
||||
|
||||
func main() {
|
||||
// Настройка конфигурации
|
||||
BVSCfg := config.config.BVSConfig{
|
||||
BVSCfg := config.BVSConfig{
|
||||
Username: "<USERNAME>",
|
||||
Password: "<PASSWORD>",
|
||||
AppID: "<APPID>",
|
||||
AppSecret: "<APPSECRET>",
|
||||
SSOURL: "https://bvs-delta.qa.loc:8443"
|
||||
SSOURL: "https://bvs-delta.qa.loc:8443",
|
||||
DecortURL: "https://mr4.digitalenergy.online",
|
||||
Domain: "dynamix",
|
||||
PathCfg: "config",
|
||||
@@ -1175,10 +1190,10 @@ func main() {
|
||||
}
|
||||
|
||||
// Создание клиента
|
||||
BVSClient := decort.NewBVS(cfg)
|
||||
BVSClient := decort.NewBVS(BVSCfg)
|
||||
|
||||
// Выполнение запроса на получение токена
|
||||
token, err := client.GetToken(context.Background())
|
||||
token, err := BVSClient.GetToken(context.Background())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -1187,7 +1202,7 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
#### Пример обновления BVS токена
|
||||
#### Пример обновления BVS токена
|
||||
|
||||
В случае указания значения в переменной конфигурации `PathCfg` обновленный токен и конфигурация будут записаны в файл в формате `json`, переменная `PathToken` служит для записи токена в файл в формате `json`
|
||||
|
||||
@@ -1203,12 +1218,12 @@ import (
|
||||
|
||||
func main() {
|
||||
// Настройка конфигурации
|
||||
BVSCfg := config.config.BVSConfig{
|
||||
BVSCfg := config.BVSConfig{
|
||||
Username: "<USERNAME>",
|
||||
Password: "<PASSWORD>",
|
||||
AppID: "<APPID>",
|
||||
AppSecret: "<APPSECRET>",
|
||||
SSOURL: "https://bvs-delta.qa.loc:8443"
|
||||
SSOURL: "https://bvs-delta.qa.loc:8443",
|
||||
DecortURL: "https://mr4.digitalenergy.online",
|
||||
Domain: "dynamix",
|
||||
PathToken: "token",
|
||||
@@ -1216,10 +1231,10 @@ func main() {
|
||||
}
|
||||
|
||||
// Создание клиента
|
||||
BVSClient := decort.NewBVS(cfg)
|
||||
BVSClient := decort.NewBVS(BVSCfg)
|
||||
|
||||
// Выполнение запроса на обновление токена
|
||||
token, err := client.RefreshToken(context.Background())
|
||||
token, err := BVSClient.RefreshToken(context.Background())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -1242,19 +1257,19 @@ import (
|
||||
|
||||
func main() {
|
||||
// Настройка конфигурации
|
||||
BVSCfg := config.config.BVSConfig{
|
||||
BVSCfg := config.BVSConfig{
|
||||
Username: "<USERNAME>",
|
||||
Password: "<PASSWORD>",
|
||||
AppID: "<APPID>",
|
||||
AppSecret: "<APPSECRET>",
|
||||
SSOURL: "https://bvs-delta.qa.loc:8443"
|
||||
SSOURL: "https://bvs-delta.qa.loc:8443",
|
||||
DecortURL: "https://mr4.digitalenergy.online",
|
||||
Domain: "dynamix",
|
||||
Retries: 5,
|
||||
}
|
||||
|
||||
// Создание клиента
|
||||
BVSClient := decort.NewBVS(cfg)
|
||||
BVSClient := decort.NewBVS(BVSCfg)
|
||||
|
||||
// Создание структуры запроса
|
||||
// CreateRequest - реквест на создание виртуальной машины
|
||||
@@ -1267,10 +1282,11 @@ func main() {
|
||||
}
|
||||
|
||||
// Выполнение запроса
|
||||
res, err := client.CloudAPI().KVMX86().Create(context.Background(), req)
|
||||
res, err := BVSClient.CloudAPI().KVMX86().Create(context.Background(), req)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fmt.Println(res)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
512
client.go
512
client.go
@@ -4,11 +4,11 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -18,12 +18,10 @@ import (
|
||||
"repository.basistech.ru/BASIS/decort-golang-sdk/config"
|
||||
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/constants"
|
||||
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi"
|
||||
k8s_ca "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/k8s"
|
||||
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker"
|
||||
k8s_cb "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/k8s"
|
||||
)
|
||||
|
||||
// HTTP-client for platform
|
||||
// DecortClient is HTTP-client for platform
|
||||
type DecortClient struct {
|
||||
decortURL string
|
||||
client *http.Client
|
||||
@@ -72,21 +70,37 @@ func (dc *DecortClient) CloudBroker() *cloudbroker.CloudBroker {
|
||||
|
||||
// DecortApiCall method for sending requests to the platform
|
||||
func (dc *DecortClient) DecortApiCall(ctx context.Context, method, url string, params interface{}) ([]byte, error) {
|
||||
k8sCaCreateReq, okCa := params.(k8s_ca.CreateRequest)
|
||||
k8sCbCreateReq, okCb := params.(k8s_cb.CreateRequest)
|
||||
var body *bytes.Buffer
|
||||
var ctype string
|
||||
values, err := query.Values(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if okCa {
|
||||
body, ctype = createK8sCloudApi(k8sCaCreateReq)
|
||||
} else if okCb {
|
||||
body, ctype = createK8sCloudBroker(k8sCbCreateReq)
|
||||
} else {
|
||||
values, err := query.Values(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body = bytes.NewBufferString(values.Encode())
|
||||
body := bytes.NewBufferString(values.Encode())
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, method, dc.decortURL+constants.Restmachine+url, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// get token
|
||||
if err = dc.getToken(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// perform request
|
||||
respBytes, err := dc.do(req, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return respBytes, err
|
||||
}
|
||||
|
||||
// DecortApiCallMP method for sending requests to the platform
|
||||
func (dc *DecortClient) DecortApiCallMP(ctx context.Context, method, url string, params interface{}) ([]byte, error) {
|
||||
body, ctype, err := multiPartReq(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, method, dc.decortURL+constants.Restmachine+url, body)
|
||||
@@ -94,320 +108,232 @@ func (dc *DecortClient) DecortApiCall(ctx context.Context, method, url string, p
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// get token
|
||||
if err = dc.getToken(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := dc.do(req, ctype)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
respBytes, err := io.ReadAll(resp.Body)
|
||||
// perform request
|
||||
respBytes, err := dc.do(req, ctype)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, errors.New(string(respBytes))
|
||||
}
|
||||
|
||||
return respBytes, nil
|
||||
return respBytes, err
|
||||
}
|
||||
|
||||
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)
|
||||
// new token is not needed
|
||||
if dc.cfg.Token != "" && !time.Now().After(dc.expiryTime) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// set up request headers and body
|
||||
body := fmt.Sprintf("grant_type=client_credentials&client_id=%s&client_secret=%s&response_type=id_token", dc.cfg.AppID, dc.cfg.AppSecret)
|
||||
bodyReader := strings.NewReader(body)
|
||||
|
||||
dc.cfg.SSOURL = strings.TrimSuffix(dc.cfg.SSOURL, "/")
|
||||
|
||||
req, _ := http.NewRequestWithContext(ctx, "POST", dc.cfg.SSOURL+"/v1/oauth/access_token", bodyReader)
|
||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
// request token
|
||||
resp, err := dc.client.Do(req)
|
||||
if err != nil || resp == nil {
|
||||
return fmt.Errorf("cannot get token: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var tokenBytes []byte
|
||||
tokenBytes, err = io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot get token: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return fmt.Errorf("cannot get token: %s", tokenBytes)
|
||||
}
|
||||
|
||||
// save token in config
|
||||
token := string(tokenBytes)
|
||||
dc.cfg.Token = token
|
||||
dc.expiryTime = time.Now().AddDate(0, 0, 1)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dc *DecortClient) do(req *http.Request, ctype string) (*http.Response, error) {
|
||||
// do method performs request and returns response as an array of bytes and nil error in case of response status code 200.
|
||||
// In any other cases do returns nil response and error.
|
||||
// Retries are implemented in case of connection reset errors.
|
||||
func (dc *DecortClient) do(req *http.Request, ctype string) ([]byte, error) {
|
||||
// set up request headers and body
|
||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
if ctype != "" {
|
||||
req.Header.Add("Content-Type", ctype)
|
||||
} else {
|
||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.Header.Set("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)
|
||||
|
||||
// for i := uint64(0); i < dc.cfg.Retries; i++ {
|
||||
// req = req.Clone(req.Context())
|
||||
req.Body = io.NopCloser(bytes.NewBuffer(buf))
|
||||
resp, err := dc.client.Do(req)
|
||||
if err != nil || resp == nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
if resp.StatusCode == 200 {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
respBytes, err := io.ReadAll(resp.Body)
|
||||
buf, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
return resp, errors.New(string(respBytes))
|
||||
|
||||
req.Body.Close()
|
||||
req.Body = io.NopCloser(bytes.NewBuffer(buf))
|
||||
|
||||
resp, err := dc.client.Do(req)
|
||||
if resp != nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
|
||||
// retries logic GOES HERE
|
||||
// get http response
|
||||
//var resp *http.Response
|
||||
//for i := uint64(0); i < dc.cfg.Retries; i++ {
|
||||
// req := req.Clone(req.Context())
|
||||
// req.Body = io.NopCloser(bytes.NewBuffer(buf))
|
||||
//
|
||||
// if i > 0 {
|
||||
// time.Sleep(5 * time.Second) // no time sleep for the first request
|
||||
// }
|
||||
//
|
||||
// resp, err = dc.client.Do(req)
|
||||
//
|
||||
// // stop retries on success and close response body
|
||||
// if resp != nil {
|
||||
// defer resp.Body.Close()
|
||||
// }
|
||||
// if err == nil {
|
||||
// break
|
||||
// }
|
||||
//
|
||||
// // retries in case of connection errors with time sleep
|
||||
// if isConnectionError(err) {
|
||||
// continue
|
||||
// }
|
||||
//
|
||||
// // return error in case of non-connection error
|
||||
// return nil, err
|
||||
//}
|
||||
|
||||
// handle http request errors
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp == nil {
|
||||
return nil, fmt.Errorf("got empty response without error")
|
||||
}
|
||||
|
||||
// handle successful request
|
||||
respBytes, _ := io.ReadAll(resp.Body)
|
||||
if resp.StatusCode == 200 {
|
||||
return respBytes, nil
|
||||
}
|
||||
|
||||
// handle errors with status code other than 200
|
||||
err = fmt.Errorf("%s", respBytes)
|
||||
return nil, fmt.Errorf("could not execute request: %w", err)
|
||||
}
|
||||
|
||||
func createK8sCloudApi(req k8s_ca.CreateRequest) (*bytes.Buffer, string) {
|
||||
// isConnectionError checks if given error falls within specific and associated connection errors
|
||||
//func isConnectionError(err error) bool {
|
||||
// if strings.Contains(err.Error(), "connection reset by peer") {
|
||||
// return true
|
||||
// }
|
||||
// if errors.Is(err, io.EOF) {
|
||||
// return true
|
||||
// }
|
||||
//
|
||||
// return false
|
||||
//}
|
||||
|
||||
// multiPartReq writes the request structure to the request body, and also returns string of the content-type
|
||||
func multiPartReq(params interface{}) (*bytes.Buffer, string, error) {
|
||||
reqBody := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(reqBody)
|
||||
if req.OidcCertificate != "" {
|
||||
part, _ := writer.CreateFormFile("oidcCertificate", "ca.crt")
|
||||
_, _ = io.Copy(part, strings.NewReader(req.OidcCertificate))
|
||||
}
|
||||
values := reflect.ValueOf(params)
|
||||
types := values.Type()
|
||||
defer writer.Close()
|
||||
for i := 0; i < values.NumField(); i++ {
|
||||
if !values.Field(i).IsValid() {
|
||||
continue
|
||||
}
|
||||
|
||||
_ = writer.WriteField("name", req.Name)
|
||||
_ = writer.WriteField("rgId", strconv.FormatUint(req.RGID, 10))
|
||||
_ = writer.WriteField("k8ciId", strconv.FormatUint(req.K8SCIID, 10))
|
||||
_ = writer.WriteField("workerGroupName", req.WorkerGroupName)
|
||||
_ = writer.WriteField("networkPlugin", req.NetworkPlugin)
|
||||
if values.Field(i).IsZero() {
|
||||
continue
|
||||
}
|
||||
|
||||
if req.MasterSEPID != 0 {
|
||||
_ = writer.WriteField("masterSepId", strconv.FormatUint(req.MasterSEPID, 10))
|
||||
}
|
||||
if req.MasterSEPPool != "" {
|
||||
_ = writer.WriteField("masterSepPool", req.MasterSEPPool)
|
||||
}
|
||||
if req.WorkerSEPID != 0 {
|
||||
_ = writer.WriteField("workerSepId", strconv.FormatUint(req.WorkerSEPID, 10))
|
||||
}
|
||||
if req.WorkerSEPPool != "" {
|
||||
_ = writer.WriteField("workerSepPool", req.WorkerSEPPool)
|
||||
}
|
||||
if file, ok := constants.FileName[types.Field(i).Name]; ok {
|
||||
part, err := writer.CreateFormFile(trimString(types.Field(i)), file)
|
||||
if err != nil {
|
||||
return &bytes.Buffer{}, "", err
|
||||
}
|
||||
_, err = io.Copy(part, strings.NewReader(valueToString(values.Field(i).Interface())))
|
||||
if err != nil {
|
||||
return &bytes.Buffer{}, "", err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if req.Labels != nil {
|
||||
for _, v := range req.Labels {
|
||||
_ = writer.WriteField("labels", v)
|
||||
if values.Field(i).Type().Kind() == reflect.Slice {
|
||||
switch slice := values.Field(i).Interface().(type) {
|
||||
case []string:
|
||||
for _, val := range slice {
|
||||
err := writer.WriteField(trimString(types.Field(i)), val)
|
||||
if err != nil {
|
||||
return &bytes.Buffer{}, "", err
|
||||
}
|
||||
}
|
||||
case []uint:
|
||||
for _, val := range slice {
|
||||
err := writer.WriteField(trimString(types.Field(i)), strconv.FormatUint(uint64(val), 10))
|
||||
if err != nil {
|
||||
return &bytes.Buffer{}, "", err
|
||||
}
|
||||
}
|
||||
case []uint64:
|
||||
for _, val := range slice {
|
||||
err := writer.WriteField(trimString(types.Field(i)), strconv.FormatUint(val, 10))
|
||||
if err != nil {
|
||||
return &bytes.Buffer{}, "", err
|
||||
}
|
||||
}
|
||||
default:
|
||||
return &bytes.Buffer{}, "", fmt.Errorf("unsupported slice type:%T", slice)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
err := writer.WriteField(trimString(types.Field(i)), valueToString(values.Field(i).Interface()))
|
||||
if err != nil {
|
||||
return &bytes.Buffer{}, "", err
|
||||
}
|
||||
}
|
||||
if req.Taints != nil {
|
||||
for _, v := range req.Taints {
|
||||
_ = writer.WriteField("taints", v)
|
||||
}
|
||||
}
|
||||
if req.Annotations != nil {
|
||||
for _, v := range req.Annotations {
|
||||
_ = writer.WriteField("annotations", v)
|
||||
}
|
||||
}
|
||||
|
||||
if req.MasterCPU != 0 {
|
||||
_ = writer.WriteField("masterCpu", strconv.FormatUint(uint64(req.MasterCPU), 10))
|
||||
}
|
||||
if req.MasterNum != 0 {
|
||||
_ = writer.WriteField("masterNum", strconv.FormatUint(uint64(req.MasterNum), 10))
|
||||
}
|
||||
if req.MasterRAM != 0 {
|
||||
_ = writer.WriteField("masterRam", strconv.FormatUint(uint64(req.MasterRAM), 10))
|
||||
}
|
||||
if req.MasterDisk != 0 {
|
||||
_ = writer.WriteField("masterDisk", strconv.FormatUint(uint64(req.MasterDisk), 10))
|
||||
}
|
||||
if req.WorkerCPU != 0 {
|
||||
_ = writer.WriteField("workerCpu", strconv.FormatUint(uint64(req.WorkerCPU), 10))
|
||||
}
|
||||
if req.WorkerNum != 0 {
|
||||
_ = writer.WriteField("workerNum", strconv.FormatUint(uint64(req.WorkerNum), 10))
|
||||
}
|
||||
if req.WorkerRAM != 0 {
|
||||
_ = writer.WriteField("workerRam", strconv.FormatUint(uint64(req.WorkerRAM), 10))
|
||||
}
|
||||
if req.WorkerDisk != 0 {
|
||||
_ = writer.WriteField("workerDisk", strconv.FormatUint(uint64(req.WorkerDisk), 10))
|
||||
}
|
||||
if req.ExtNetID != 0 {
|
||||
_ = writer.WriteField("extnetId", strconv.FormatUint(req.ExtNetID, 10))
|
||||
}
|
||||
if req.VinsId != 0 {
|
||||
_ = writer.WriteField("vinsId", strconv.FormatUint(req.VinsId, 10))
|
||||
}
|
||||
if !req.WithLB {
|
||||
_ = writer.WriteField("withLB", strconv.FormatBool(req.WithLB))
|
||||
}
|
||||
|
||||
_ = writer.WriteField("highlyAvailableLB", strconv.FormatBool(req.HighlyAvailable))
|
||||
|
||||
if req.AdditionalSANs != nil {
|
||||
for _, v := range req.AdditionalSANs {
|
||||
_ = writer.WriteField("additionalSANs", v)
|
||||
}
|
||||
}
|
||||
if req.InitConfiguration != "" {
|
||||
_ = writer.WriteField("initConfiguration", req.InitConfiguration)
|
||||
}
|
||||
if req.ClusterConfiguration != "" {
|
||||
_ = writer.WriteField("clusterConfiguration", req.ClusterConfiguration)
|
||||
}
|
||||
if req.KubeletConfiguration != "" {
|
||||
_ = writer.WriteField("kubeletConfiguration", req.KubeletConfiguration)
|
||||
}
|
||||
if req.KubeProxyConfiguration != "" {
|
||||
_ = writer.WriteField("kubeProxyConfiguration", req.KubeProxyConfiguration)
|
||||
}
|
||||
if req.JoinConfiguration != "" {
|
||||
_ = writer.WriteField("joinConfiguration", req.JoinConfiguration)
|
||||
}
|
||||
if req.Description != "" {
|
||||
_ = writer.WriteField("desc", req.Description)
|
||||
}
|
||||
if req.UserData != "" {
|
||||
_ = writer.WriteField("userData", req.UserData)
|
||||
}
|
||||
|
||||
_ = writer.WriteField("extnetOnly", strconv.FormatBool(req.ExtNetOnly))
|
||||
|
||||
ct := writer.FormDataContentType()
|
||||
writer.Close()
|
||||
|
||||
return reqBody, ct
|
||||
return reqBody, ct, nil
|
||||
}
|
||||
|
||||
func createK8sCloudBroker(req k8s_cb.CreateRequest) (*bytes.Buffer, string) {
|
||||
reqBody := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(reqBody)
|
||||
if req.OidcCertificate != "" {
|
||||
part, _ := writer.CreateFormFile("oidcCertificate", "ca.crt")
|
||||
_, _ = io.Copy(part, strings.NewReader(req.OidcCertificate))
|
||||
func valueToString(a any) string {
|
||||
switch str := a.(type) {
|
||||
case string:
|
||||
return str
|
||||
case uint:
|
||||
return strconv.FormatUint(uint64(str), 10)
|
||||
case uint64:
|
||||
return strconv.FormatUint(str, 10)
|
||||
case bool:
|
||||
return strconv.FormatBool(str)
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
||||
_ = writer.WriteField("name", req.Name)
|
||||
_ = writer.WriteField("rgId", strconv.FormatUint(req.RGID, 10))
|
||||
_ = writer.WriteField("k8ciId", strconv.FormatUint(req.K8CIID, 10))
|
||||
_ = writer.WriteField("workerGroupName", req.WorkerGroupName)
|
||||
_ = writer.WriteField("networkPlugin", req.NetworkPlugin)
|
||||
|
||||
if req.MasterSEPID != 0 {
|
||||
_ = writer.WriteField("masterSepId", strconv.FormatUint(req.MasterSEPID, 10))
|
||||
}
|
||||
if req.MasterSEPPool != "" {
|
||||
_ = writer.WriteField("masterSepPool", req.MasterSEPPool)
|
||||
}
|
||||
if req.WorkerSEPID != 0 {
|
||||
_ = writer.WriteField("workerSepId", strconv.FormatUint(req.WorkerSEPID, 10))
|
||||
}
|
||||
if req.WorkerSEPPool != "" {
|
||||
_ = writer.WriteField("workerSepPool", req.WorkerSEPPool)
|
||||
}
|
||||
|
||||
if req.Labels != nil {
|
||||
for _, v := range req.Labels {
|
||||
_ = writer.WriteField("labels", v)
|
||||
}
|
||||
}
|
||||
if req.Taints != nil {
|
||||
for _, v := range req.Taints {
|
||||
_ = writer.WriteField("taints", v)
|
||||
}
|
||||
}
|
||||
if req.Annotations != nil {
|
||||
for _, v := range req.Annotations {
|
||||
_ = writer.WriteField("annotations", v)
|
||||
}
|
||||
}
|
||||
|
||||
if req.MasterCPU != 0 {
|
||||
_ = writer.WriteField("masterCpu", strconv.FormatUint(req.MasterCPU, 10))
|
||||
}
|
||||
if req.MasterNum != 0 {
|
||||
_ = writer.WriteField("masterNum", strconv.FormatUint(req.MasterNum, 10))
|
||||
}
|
||||
if req.MasterRAM != 0 {
|
||||
_ = writer.WriteField("masterRam", strconv.FormatUint(req.MasterRAM, 10))
|
||||
}
|
||||
if req.MasterDisk != 0 {
|
||||
_ = writer.WriteField("masterDisk", strconv.FormatUint(req.MasterDisk, 10))
|
||||
}
|
||||
if req.WorkerCPU != 0 {
|
||||
_ = writer.WriteField("workerCpu", strconv.FormatUint(req.WorkerCPU, 10))
|
||||
}
|
||||
if req.WorkerNum != 0 {
|
||||
_ = writer.WriteField("workerNum", strconv.FormatUint(req.WorkerNum, 10))
|
||||
}
|
||||
if req.WorkerRAM != 0 {
|
||||
_ = writer.WriteField("workerRam", strconv.FormatUint(req.WorkerRAM, 10))
|
||||
}
|
||||
if req.WorkerDisk != 0 {
|
||||
_ = writer.WriteField("workerDisk", strconv.FormatUint(req.WorkerDisk, 10))
|
||||
}
|
||||
if req.ExtNetID != 0 {
|
||||
_ = writer.WriteField("extnetId", strconv.FormatUint(req.ExtNetID, 10))
|
||||
}
|
||||
if req.VinsId != 0 {
|
||||
_ = writer.WriteField("vinsId", strconv.FormatUint(req.VinsId, 10))
|
||||
}
|
||||
if !req.WithLB {
|
||||
_ = writer.WriteField("withLB", strconv.FormatBool(req.WithLB))
|
||||
}
|
||||
|
||||
_ = writer.WriteField("highlyAvailableLB", strconv.FormatBool(req.HighlyAvailable))
|
||||
|
||||
if req.AdditionalSANs != nil {
|
||||
for _, v := range req.AdditionalSANs {
|
||||
_ = writer.WriteField("additionalSANs", v)
|
||||
}
|
||||
}
|
||||
if req.InitConfiguration != "" {
|
||||
_ = writer.WriteField("initConfiguration", req.InitConfiguration)
|
||||
}
|
||||
if req.ClusterConfiguration != "" {
|
||||
_ = writer.WriteField("clusterConfiguration", req.ClusterConfiguration)
|
||||
}
|
||||
if req.KubeletConfiguration != "" {
|
||||
_ = writer.WriteField("kubeletConfiguration", req.KubeletConfiguration)
|
||||
}
|
||||
if req.KubeProxyConfiguration != "" {
|
||||
_ = writer.WriteField("kubeProxyConfiguration", req.KubeProxyConfiguration)
|
||||
}
|
||||
if req.JoinConfiguration != "" {
|
||||
_ = writer.WriteField("joinConfiguration", req.JoinConfiguration)
|
||||
}
|
||||
if req.Description != "" {
|
||||
_ = writer.WriteField("desc", req.Description)
|
||||
}
|
||||
if req.UserData != "" {
|
||||
_ = writer.WriteField("userData", req.UserData)
|
||||
}
|
||||
|
||||
_ = writer.WriteField("extnetOnly", strconv.FormatBool(req.ExtNetOnly))
|
||||
|
||||
ct := writer.FormDataContentType()
|
||||
|
||||
writer.Close()
|
||||
return reqBody, ct
|
||||
}
|
||||
|
||||
func trimString(el reflect.StructField) string {
|
||||
return strings.TrimSuffix(el.Tag.Get("url"), ",omitempty")
|
||||
}
|
||||
|
||||
473
client_bvs.go
473
client_bvs.go
@@ -8,9 +8,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -19,12 +17,10 @@ import (
|
||||
"repository.basistech.ru/BASIS/decort-golang-sdk/config"
|
||||
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/constants"
|
||||
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi"
|
||||
k8s_ca "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/k8s"
|
||||
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker"
|
||||
k8s_cb "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/k8s"
|
||||
)
|
||||
|
||||
// HTTP-client for platform
|
||||
// BVSDecortClient is HTTP-client for platform
|
||||
type BVSDecortClient struct {
|
||||
client *http.Client
|
||||
cfg config.BVSConfig
|
||||
@@ -75,34 +71,26 @@ func (bdc *BVSDecortClient) CloudBroker() *cloudbroker.CloudBroker {
|
||||
|
||||
// DecortApiCall method for sending requests to the platform
|
||||
func (bdc *BVSDecortClient) DecortApiCall(ctx context.Context, method, url string, params interface{}) ([]byte, error) {
|
||||
k8sCaCreateReq, okCa := params.(k8s_ca.CreateRequest)
|
||||
k8sCbCreateReq, okCb := params.(k8s_cb.CreateRequest)
|
||||
var body *bytes.Buffer
|
||||
var ctype string
|
||||
|
||||
if okCa {
|
||||
body, ctype = createK8sCloudApiBVS(k8sCaCreateReq)
|
||||
} else if okCb {
|
||||
body, ctype = createK8sCloudBrokerBVS(k8sCbCreateReq)
|
||||
} else {
|
||||
values, err := query.Values(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body = bytes.NewBufferString(values.Encode())
|
||||
values, err := query.Values(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body := bytes.NewBufferString(values.Encode())
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, method, bdc.decortURL+constants.Restmachine+url, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// get token
|
||||
if bdc.cfg.Token.AccessToken == "" {
|
||||
if _, err = bdc.GetToken(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// refresh token
|
||||
if bdc.cfg.Token.RefreshToken != "" && bdc.cfg.Token.Expiry.Add(-time.Duration(bdc.cfg.TimeToRefresh)*time.Minute).Before(time.Now()) {
|
||||
if _, err := bdc.RefreshToken(ctx); err != nil {
|
||||
if _, err = bdc.GetToken(ctx); err != nil {
|
||||
@@ -110,46 +98,87 @@ func (bdc *BVSDecortClient) DecortApiCall(ctx context.Context, method, url strin
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// perform request
|
||||
reqCopy := req.Clone(ctx)
|
||||
//nolint:bodyclose
|
||||
//work defer, error lint
|
||||
resp, err := bdc.do(req, ctype)
|
||||
if err != nil {
|
||||
if err.Error() == "access is denied" {
|
||||
if _, err = bdc.GetToken(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//nolint:bodyclose
|
||||
//we close the body in case of any error
|
||||
resp, err = bdc.do(reqCopy, ctype)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
respBytes, err := bdc.do(req, "")
|
||||
if err == nil {
|
||||
return respBytes, nil
|
||||
}
|
||||
|
||||
// get token and retry in case of access denied
|
||||
if err.Error() == "access is denied" {
|
||||
_, err = bdc.GetToken(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
respBytes, err = bdc.do(reqCopy, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
respBytes, err := io.ReadAll(resp.Body)
|
||||
return respBytes, err
|
||||
}
|
||||
|
||||
func (bdc *BVSDecortClient) DecortApiCallMP(ctx context.Context, method, url string, params interface{}) ([]byte, error) {
|
||||
body, ctype, err := multiPartReq(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, errors.New(string(respBytes))
|
||||
req, err := http.NewRequestWithContext(ctx, method, bdc.decortURL+constants.Restmachine+url, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return respBytes, nil
|
||||
// get token
|
||||
if bdc.cfg.Token.AccessToken == "" {
|
||||
if _, err = bdc.GetToken(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// refresh token
|
||||
if bdc.cfg.Token.RefreshToken != "" && bdc.cfg.Token.Expiry.Add(-time.Duration(bdc.cfg.TimeToRefresh)*time.Minute).Before(time.Now()) {
|
||||
if _, err := bdc.RefreshToken(ctx); err != nil {
|
||||
if _, err = bdc.GetToken(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// perform request
|
||||
reqCopy := req.Clone(ctx)
|
||||
respBytes, err := bdc.do(req, ctype)
|
||||
if err == nil {
|
||||
return respBytes, nil
|
||||
}
|
||||
|
||||
// get token and retry in case of access denied
|
||||
if err.Error() == "access is denied" {
|
||||
_, err = bdc.GetToken(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
respBytes, err = bdc.do(reqCopy, ctype)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return respBytes, err
|
||||
}
|
||||
|
||||
// GetToken allows you to get a token and returns the token structure, when specifying the PathCfg variable,
|
||||
// the token and configuration will be written to a file,
|
||||
// when specifying the PathToken variable, the token will be written to a file
|
||||
// GetToken allows you to get a token and returns the token structure. When specifying the PathCfg variable,
|
||||
// the token and configuration will be written to a file.
|
||||
// When specifying the PathToken variable, the token will be written to a file.
|
||||
func (bdc *BVSDecortClient) GetToken(ctx context.Context) (config.Token, error) {
|
||||
bdc.mutex.Lock()
|
||||
defer bdc.mutex.Unlock()
|
||||
|
||||
// set up request headers and body
|
||||
body := fmt.Sprintf("grant_type=password&client_id=%s&client_secret=%s&username=%s&password=%s&response_type=token&scope=openid", bdc.cfg.AppID, bdc.cfg.AppSecret, bdc.cfg.Username, bdc.cfg.Password)
|
||||
bodyReader := strings.NewReader(body)
|
||||
|
||||
@@ -158,20 +187,25 @@ func (bdc *BVSDecortClient) GetToken(ctx context.Context) (config.Token, error)
|
||||
req, _ := http.NewRequestWithContext(ctx, "POST", bdc.cfg.SSOURL+"/realms/"+bdc.cfg.Domain+"/protocol/openid-connect/token", bodyReader)
|
||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
// request token
|
||||
resp, err := bdc.client.Do(req)
|
||||
if err != nil || resp == nil {
|
||||
return config.Token{}, fmt.Errorf("cannot get token: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var tokenBytes []byte
|
||||
tokenBytes, err = io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return config.Token{}, fmt.Errorf("cannot get token: %w", err)
|
||||
}
|
||||
|
||||
tokenBytes, _ := io.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return config.Token{}, fmt.Errorf("cannot get token: %s", tokenBytes)
|
||||
}
|
||||
|
||||
// save token in config
|
||||
var tj tokenJSON
|
||||
|
||||
if err = json.Unmarshal(tokenBytes, &tj); err != nil {
|
||||
return config.Token{}, fmt.Errorf("cannot unmarshal token: %w", err)
|
||||
}
|
||||
@@ -196,13 +230,14 @@ func (bdc *BVSDecortClient) GetToken(ctx context.Context) (config.Token, error)
|
||||
return bdc.cfg.Token, nil
|
||||
}
|
||||
|
||||
// RefreshToken allows you to refresh a token and returns the token structure, when specifying the PathCfg variable,
|
||||
// the token and configuration will be written to a file,
|
||||
// when specifying the PathToken variable, the token will be written to a file
|
||||
// RefreshToken allows you to refresh a token and returns the token structure. When specifying the PathCfg variable,
|
||||
// the token and configuration will be written to a file.
|
||||
// When specifying the PathToken variable, the token will be written to a file
|
||||
func (bdc *BVSDecortClient) RefreshToken(ctx context.Context) (config.Token, error) {
|
||||
bdc.mutex.Lock()
|
||||
defer bdc.mutex.Unlock()
|
||||
|
||||
// set up request headers and body
|
||||
body := fmt.Sprintf("grant_type=refresh_token&client_id=%s&client_secret=%s&refresh_token=%s&scope=openid", bdc.cfg.AppID, bdc.cfg.AppSecret, bdc.cfg.Token.RefreshToken)
|
||||
bodyReader := strings.NewReader(body)
|
||||
|
||||
@@ -211,20 +246,25 @@ func (bdc *BVSDecortClient) RefreshToken(ctx context.Context) (config.Token, err
|
||||
req, _ := http.NewRequestWithContext(ctx, "POST", bdc.cfg.SSOURL+"/realms/"+bdc.cfg.Domain+"/protocol/openid-connect/token", bodyReader)
|
||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
// refresh token
|
||||
resp, err := bdc.client.Do(req)
|
||||
if err != nil || resp == nil {
|
||||
return config.Token{}, fmt.Errorf("cannot refresh token: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var tokenBytes []byte
|
||||
tokenBytes, err = io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return config.Token{}, fmt.Errorf("cannot refresh token: %w", err)
|
||||
}
|
||||
|
||||
tokenBytes, _ := io.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return config.Token{}, fmt.Errorf("cannot refresh token: %s", tokenBytes)
|
||||
}
|
||||
|
||||
// save token in config
|
||||
var tj tokenJSON
|
||||
|
||||
if err = json.Unmarshal(tokenBytes, &tj); err != nil {
|
||||
return config.Token{}, fmt.Errorf("cannot unmarshal after refresh token: %w", err)
|
||||
}
|
||||
@@ -256,264 +296,85 @@ func (e *tokenJSON) expiry() (t time.Time) {
|
||||
return
|
||||
}
|
||||
|
||||
func (bdc *BVSDecortClient) do(req *http.Request, ctype string) (*http.Response, error) {
|
||||
// do method performs request and returns response as an array of bytes and nil error in case of response status code 200.
|
||||
// In any other cases do returns nil response and error.
|
||||
// Retries are implemented in case of connection reset errors.
|
||||
func (bdc *BVSDecortClient) do(req *http.Request, ctype string) ([]byte, error) {
|
||||
// set up request headers and body
|
||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
if ctype != "" {
|
||||
req.Header.Add("Content-Type", ctype)
|
||||
} else {
|
||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.Header.Set("Content-Type", ctype)
|
||||
}
|
||||
|
||||
req.Header.Add("Authorization", "bearer "+bdc.cfg.Token.AccessToken)
|
||||
req.Header.Set("Accept", "application/json")
|
||||
|
||||
buf, _ := io.ReadAll(req.Body)
|
||||
|
||||
req.Body = io.NopCloser(bytes.NewBuffer(buf))
|
||||
resp, err := bdc.client.Do(req)
|
||||
if err != nil || resp == nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
if resp.StatusCode == 401 {
|
||||
resp.Body.Close()
|
||||
return resp, errors.New("access is denied")
|
||||
}
|
||||
|
||||
if resp.StatusCode == 200 {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
respBytes, err := io.ReadAll(resp.Body)
|
||||
buf, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
return resp, errors.New(string(respBytes))
|
||||
}
|
||||
|
||||
func createK8sCloudApiBVS(req k8s_ca.CreateRequest) (*bytes.Buffer, string) {
|
||||
reqBody := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(reqBody)
|
||||
if req.OidcCertificate != "" {
|
||||
part, _ := writer.CreateFormFile("oidcCertificate", "ca.crt")
|
||||
_, _ = io.Copy(part, strings.NewReader(req.OidcCertificate))
|
||||
}
|
||||
|
||||
_ = writer.WriteField("name", req.Name)
|
||||
_ = writer.WriteField("rgId", strconv.FormatUint(req.RGID, 10))
|
||||
_ = writer.WriteField("k8ciId", strconv.FormatUint(req.K8SCIID, 10))
|
||||
_ = writer.WriteField("workerGroupName", req.WorkerGroupName)
|
||||
_ = writer.WriteField("networkPlugin", req.NetworkPlugin)
|
||||
|
||||
if req.MasterSEPID != 0 {
|
||||
_ = writer.WriteField("masterSepId", strconv.FormatUint(req.MasterSEPID, 10))
|
||||
}
|
||||
if req.MasterSEPPool != "" {
|
||||
_ = writer.WriteField("masterSepPool", req.MasterSEPPool)
|
||||
}
|
||||
if req.WorkerSEPID != 0 {
|
||||
_ = writer.WriteField("workerSepId", strconv.FormatUint(req.WorkerSEPID, 10))
|
||||
}
|
||||
if req.WorkerSEPPool != "" {
|
||||
_ = writer.WriteField("workerSepPool", req.WorkerSEPPool)
|
||||
}
|
||||
|
||||
if req.Labels != nil {
|
||||
for _, v := range req.Labels {
|
||||
_ = writer.WriteField("labels", v)
|
||||
}
|
||||
}
|
||||
if req.Taints != nil {
|
||||
for _, v := range req.Taints {
|
||||
_ = writer.WriteField("taints", v)
|
||||
}
|
||||
}
|
||||
if req.Annotations != nil {
|
||||
for _, v := range req.Annotations {
|
||||
_ = writer.WriteField("annotations", v)
|
||||
}
|
||||
}
|
||||
|
||||
if req.MasterCPU != 0 {
|
||||
_ = writer.WriteField("masterCpu", strconv.FormatUint(uint64(req.MasterCPU), 10))
|
||||
}
|
||||
if req.MasterNum != 0 {
|
||||
_ = writer.WriteField("masterNum", strconv.FormatUint(uint64(req.MasterNum), 10))
|
||||
}
|
||||
if req.MasterRAM != 0 {
|
||||
_ = writer.WriteField("masterRam", strconv.FormatUint(uint64(req.MasterRAM), 10))
|
||||
}
|
||||
if req.MasterDisk != 0 {
|
||||
_ = writer.WriteField("masterDisk", strconv.FormatUint(uint64(req.MasterDisk), 10))
|
||||
}
|
||||
if req.WorkerCPU != 0 {
|
||||
_ = writer.WriteField("workerCpu", strconv.FormatUint(uint64(req.WorkerCPU), 10))
|
||||
}
|
||||
if req.WorkerNum != 0 {
|
||||
_ = writer.WriteField("workerNum", strconv.FormatUint(uint64(req.WorkerNum), 10))
|
||||
}
|
||||
if req.WorkerRAM != 0 {
|
||||
_ = writer.WriteField("workerRam", strconv.FormatUint(uint64(req.WorkerRAM), 10))
|
||||
}
|
||||
if req.WorkerDisk != 0 {
|
||||
_ = writer.WriteField("workerDisk", strconv.FormatUint(uint64(req.WorkerDisk), 10))
|
||||
}
|
||||
if req.ExtNetID != 0 {
|
||||
_ = writer.WriteField("extnetId", strconv.FormatUint(req.ExtNetID, 10))
|
||||
}
|
||||
if req.VinsId != 0 {
|
||||
_ = writer.WriteField("vinsId", strconv.FormatUint(req.VinsId, 10))
|
||||
}
|
||||
if !req.WithLB {
|
||||
_ = writer.WriteField("withLB", strconv.FormatBool(req.WithLB))
|
||||
}
|
||||
|
||||
_ = writer.WriteField("highlyAvailableLB", strconv.FormatBool(req.HighlyAvailable))
|
||||
|
||||
if req.AdditionalSANs != nil {
|
||||
for _, v := range req.AdditionalSANs {
|
||||
_ = writer.WriteField("additionalSANs", v)
|
||||
}
|
||||
}
|
||||
if req.InitConfiguration != "" {
|
||||
_ = writer.WriteField("initConfiguration", req.InitConfiguration)
|
||||
}
|
||||
if req.ClusterConfiguration != "" {
|
||||
_ = writer.WriteField("clusterConfiguration", req.ClusterConfiguration)
|
||||
}
|
||||
if req.KubeletConfiguration != "" {
|
||||
_ = writer.WriteField("kubeletConfiguration", req.KubeletConfiguration)
|
||||
}
|
||||
if req.KubeProxyConfiguration != "" {
|
||||
_ = writer.WriteField("kubeProxyConfiguration", req.KubeProxyConfiguration)
|
||||
}
|
||||
if req.JoinConfiguration != "" {
|
||||
_ = writer.WriteField("joinConfiguration", req.JoinConfiguration)
|
||||
}
|
||||
if req.Description != "" {
|
||||
_ = writer.WriteField("desc", req.Description)
|
||||
}
|
||||
if req.UserData != "" {
|
||||
_ = writer.WriteField("userData", req.UserData)
|
||||
}
|
||||
|
||||
_ = writer.WriteField("extnetOnly", strconv.FormatBool(req.ExtNetOnly))
|
||||
|
||||
ct := writer.FormDataContentType()
|
||||
writer.Close()
|
||||
|
||||
return reqBody, ct
|
||||
}
|
||||
|
||||
func createK8sCloudBrokerBVS(req k8s_cb.CreateRequest) (*bytes.Buffer, string) {
|
||||
reqBody := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(reqBody)
|
||||
if req.OidcCertificate != "" {
|
||||
part, _ := writer.CreateFormFile("oidcCertificate", "ca.crt")
|
||||
_, _ = io.Copy(part, strings.NewReader(req.OidcCertificate))
|
||||
}
|
||||
|
||||
_ = writer.WriteField("name", req.Name)
|
||||
_ = writer.WriteField("rgId", strconv.FormatUint(req.RGID, 10))
|
||||
_ = writer.WriteField("k8ciId", strconv.FormatUint(req.K8CIID, 10))
|
||||
_ = writer.WriteField("workerGroupName", req.WorkerGroupName)
|
||||
_ = writer.WriteField("networkPlugin", req.NetworkPlugin)
|
||||
|
||||
if req.MasterSEPID != 0 {
|
||||
_ = writer.WriteField("masterSepId", strconv.FormatUint(req.MasterSEPID, 10))
|
||||
}
|
||||
if req.MasterSEPPool != "" {
|
||||
_ = writer.WriteField("masterSepPool", req.MasterSEPPool)
|
||||
}
|
||||
if req.WorkerSEPID != 0 {
|
||||
_ = writer.WriteField("workerSepId", strconv.FormatUint(req.WorkerSEPID, 10))
|
||||
}
|
||||
if req.WorkerSEPPool != "" {
|
||||
_ = writer.WriteField("workerSepPool", req.WorkerSEPPool)
|
||||
}
|
||||
|
||||
if req.Labels != nil {
|
||||
for _, v := range req.Labels {
|
||||
_ = writer.WriteField("labels", v)
|
||||
}
|
||||
}
|
||||
if req.Taints != nil {
|
||||
for _, v := range req.Taints {
|
||||
_ = writer.WriteField("taints", v)
|
||||
}
|
||||
}
|
||||
if req.Annotations != nil {
|
||||
for _, v := range req.Annotations {
|
||||
_ = writer.WriteField("annotations", v)
|
||||
}
|
||||
}
|
||||
|
||||
if req.MasterCPU != 0 {
|
||||
_ = writer.WriteField("masterCpu", strconv.FormatUint(req.MasterCPU, 10))
|
||||
}
|
||||
if req.MasterNum != 0 {
|
||||
_ = writer.WriteField("masterNum", strconv.FormatUint(req.MasterNum, 10))
|
||||
}
|
||||
if req.MasterRAM != 0 {
|
||||
_ = writer.WriteField("masterRam", strconv.FormatUint(req.MasterRAM, 10))
|
||||
}
|
||||
if req.MasterDisk != 0 {
|
||||
_ = writer.WriteField("masterDisk", strconv.FormatUint(req.MasterDisk, 10))
|
||||
}
|
||||
if req.WorkerCPU != 0 {
|
||||
_ = writer.WriteField("workerCpu", strconv.FormatUint(req.WorkerCPU, 10))
|
||||
}
|
||||
if req.WorkerNum != 0 {
|
||||
_ = writer.WriteField("workerNum", strconv.FormatUint(req.WorkerNum, 10))
|
||||
}
|
||||
if req.WorkerRAM != 0 {
|
||||
_ = writer.WriteField("workerRam", strconv.FormatUint(req.WorkerRAM, 10))
|
||||
}
|
||||
if req.WorkerDisk != 0 {
|
||||
_ = writer.WriteField("workerDisk", strconv.FormatUint(req.WorkerDisk, 10))
|
||||
}
|
||||
if req.ExtNetID != 0 {
|
||||
_ = writer.WriteField("extnetId", strconv.FormatUint(req.ExtNetID, 10))
|
||||
}
|
||||
if req.VinsId != 0 {
|
||||
_ = writer.WriteField("vinsId", strconv.FormatUint(req.VinsId, 10))
|
||||
}
|
||||
if !req.WithLB {
|
||||
_ = writer.WriteField("withLB", strconv.FormatBool(req.WithLB))
|
||||
}
|
||||
|
||||
_ = writer.WriteField("highlyAvailableLB", strconv.FormatBool(req.HighlyAvailable))
|
||||
|
||||
if req.AdditionalSANs != nil {
|
||||
for _, v := range req.AdditionalSANs {
|
||||
_ = writer.WriteField("additionalSANs", v)
|
||||
}
|
||||
}
|
||||
if req.InitConfiguration != "" {
|
||||
_ = writer.WriteField("initConfiguration", req.InitConfiguration)
|
||||
}
|
||||
if req.ClusterConfiguration != "" {
|
||||
_ = writer.WriteField("clusterConfiguration", req.ClusterConfiguration)
|
||||
}
|
||||
if req.KubeletConfiguration != "" {
|
||||
_ = writer.WriteField("kubeletConfiguration", req.KubeletConfiguration)
|
||||
}
|
||||
if req.KubeProxyConfiguration != "" {
|
||||
_ = writer.WriteField("kubeProxyConfiguration", req.KubeProxyConfiguration)
|
||||
}
|
||||
if req.JoinConfiguration != "" {
|
||||
_ = writer.WriteField("joinConfiguration", req.JoinConfiguration)
|
||||
}
|
||||
if req.Description != "" {
|
||||
_ = writer.WriteField("desc", req.Description)
|
||||
}
|
||||
if req.UserData != "" {
|
||||
_ = writer.WriteField("userData", req.UserData)
|
||||
}
|
||||
|
||||
_ = writer.WriteField("extnetOnly", strconv.FormatBool(req.ExtNetOnly))
|
||||
|
||||
ct := writer.FormDataContentType()
|
||||
|
||||
writer.Close()
|
||||
return reqBody, ct
|
||||
|
||||
req.Body.Close()
|
||||
req.Body = io.NopCloser(bytes.NewBuffer(buf))
|
||||
|
||||
resp, err := bdc.client.Do(req)
|
||||
if resp != nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
|
||||
// retries logic GOES HERE
|
||||
// get http response
|
||||
//var resp *http.Response
|
||||
//for i := uint64(0); i < bdc.cfg.Retries; i++ {
|
||||
// req := req.Clone(req.Context())
|
||||
// req.Body = io.NopCloser(bytes.NewBuffer(buf))
|
||||
//
|
||||
// if i > 0 {
|
||||
// time.Sleep(5 * time.Second) // no time sleep for the first request
|
||||
// }
|
||||
//
|
||||
// resp, err = bdc.client.Do(req)
|
||||
//
|
||||
// // stop retries on success and close response body
|
||||
// if resp != nil {
|
||||
// defer resp.Body.Close()
|
||||
// }
|
||||
// if err == nil {
|
||||
// break
|
||||
// }
|
||||
//
|
||||
// // retries in case of connection errors with time sleep
|
||||
// if isConnectionError(err) {
|
||||
// continue
|
||||
// }
|
||||
//
|
||||
// // return error in case of non-connection error
|
||||
// return nil, err
|
||||
//}
|
||||
|
||||
// handle http request errors
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp == nil {
|
||||
return nil, fmt.Errorf("got empty response without error")
|
||||
}
|
||||
|
||||
var respBytes []byte
|
||||
respBytes, err = io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// handle access denied and successful request
|
||||
if resp.StatusCode == 401 {
|
||||
return respBytes, errors.New("access is denied")
|
||||
}
|
||||
if resp.StatusCode == 200 {
|
||||
return respBytes, nil
|
||||
}
|
||||
|
||||
// handle errors with other status codes
|
||||
err = fmt.Errorf("%s", respBytes)
|
||||
return nil, fmt.Errorf("could not execute request: %w", err)
|
||||
}
|
||||
|
||||
@@ -6,4 +6,7 @@ import "context"
|
||||
type Caller interface {
|
||||
// DecortApiCall method for sending requests to the platform
|
||||
DecortApiCall(ctx context.Context, method, url string, params interface{}) ([]byte, error)
|
||||
|
||||
// DecortApiCallMP method for sending requests to the platform
|
||||
DecortApiCallMP(ctx context.Context, method, url string, params interface{}) ([]byte, error)
|
||||
}
|
||||
|
||||
@@ -2,4 +2,6 @@ package constants
|
||||
|
||||
const Restmachine = "/restmachine"
|
||||
|
||||
|
||||
var FileName = map[string]string{
|
||||
"OidcCertificate": "ca.crt",
|
||||
}
|
||||
|
||||
433
legacy-client.go
433
legacy-client.go
@@ -4,13 +4,10 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -19,12 +16,10 @@ import (
|
||||
"repository.basistech.ru/BASIS/decort-golang-sdk/config"
|
||||
"repository.basistech.ru/BASIS/decort-golang-sdk/internal/constants"
|
||||
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi"
|
||||
k8s_ca "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/k8s"
|
||||
"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker"
|
||||
k8s_cb "repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/k8s"
|
||||
)
|
||||
|
||||
// Legacy HTTP-client for platform
|
||||
// LegacyDecortClient is Legacy HTTP-client for platform
|
||||
type LegacyDecortClient struct {
|
||||
decortURL string
|
||||
client *http.Client
|
||||
@@ -73,26 +68,36 @@ func (ldc *LegacyDecortClient) CloudBroker() *cloudbroker.CloudBroker {
|
||||
|
||||
// DecortApiCall method for sending requests to the platform
|
||||
func (ldc *LegacyDecortClient) DecortApiCall(ctx context.Context, method, url string, params interface{}) ([]byte, error) {
|
||||
// get token
|
||||
if err := ldc.getToken(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
k8sCaCreateReq, okCa := params.(k8s_ca.CreateRequest)
|
||||
k8sCbCreateReq, okCb := params.(k8s_cb.CreateRequest)
|
||||
values, err := query.Values(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var body *bytes.Buffer
|
||||
var ctype string
|
||||
body := bytes.NewBufferString(values.Encode() + fmt.Sprintf("&authkey=%s", ldc.cfg.Token))
|
||||
|
||||
if okCa {
|
||||
body, ctype = createK8sCloudApiLegacy(k8sCaCreateReq, ldc.cfg.Token)
|
||||
} else if okCb {
|
||||
body, ctype = createK8sCloudBrokerLegacy(k8sCbCreateReq, ldc.cfg.Token)
|
||||
} else {
|
||||
values, err := query.Values(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body = bytes.NewBufferString(values.Encode() + fmt.Sprintf("&authkey=%s", ldc.cfg.Token))
|
||||
req, err := http.NewRequestWithContext(ctx, method, ldc.decortURL+constants.Restmachine+url, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// perform request
|
||||
respBytes, err := ldc.do(req, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return respBytes, err
|
||||
}
|
||||
|
||||
func (ldc *LegacyDecortClient) DecortApiCallMP(ctx context.Context, method, url string, params interface{}) ([]byte, error) {
|
||||
body, ctype, err := multiPartReq(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, method, ldc.decortURL+constants.Restmachine+url, body)
|
||||
@@ -100,315 +105,131 @@ func (ldc *LegacyDecortClient) DecortApiCall(ctx context.Context, method, url st
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := ldc.do(req, ctype)
|
||||
if err != nil {
|
||||
// get token
|
||||
if err = ldc.getToken(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
respBytes, err := io.ReadAll(resp.Body)
|
||||
// perform request
|
||||
respBytes, err := ldc.do(req, ctype)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return nil, errors.New(string(respBytes))
|
||||
}
|
||||
|
||||
return respBytes, nil
|
||||
return respBytes, err
|
||||
}
|
||||
|
||||
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)
|
||||
// new token is not needed
|
||||
if ldc.cfg.Token != "" && !time.Now().After(ldc.expiryTime) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// set up request headers and body
|
||||
body := fmt.Sprintf("username=%s&password=%s", url.QueryEscape(ldc.cfg.Username), url.QueryEscape(ldc.cfg.Password))
|
||||
bodyReader := strings.NewReader(body)
|
||||
|
||||
req, _ := http.NewRequestWithContext(ctx, "POST", ldc.cfg.DecortURL+"/restmachine/cloudapi/user/authenticate", bodyReader)
|
||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
// request token
|
||||
resp, err := ldc.client.Do(req)
|
||||
if err != nil || resp == nil {
|
||||
return fmt.Errorf("cannot get token: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var tokenBytes []byte
|
||||
tokenBytes, err = io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot get token: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != 200 {
|
||||
return fmt.Errorf("cannot get token: %s", tokenBytes)
|
||||
}
|
||||
|
||||
// save token in config
|
||||
token := string(tokenBytes)
|
||||
ldc.cfg.Token = token
|
||||
ldc.expiryTime = time.Now().AddDate(0, 0, 1)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ldc *LegacyDecortClient) do(req *http.Request, ctype string) (*http.Response, error) {
|
||||
// do method performs request and returns response as an array of bytes and nil error in case of response status code 200.
|
||||
// In any other cases do returns nil response and error.
|
||||
// Retries are implemented in case of connection reset errors.
|
||||
func (ldc *LegacyDecortClient) do(req *http.Request, ctype string) ([]byte, error) {
|
||||
// set up request headers and body
|
||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
if ctype != "" {
|
||||
req.Header.Add("Content-Type", ctype)
|
||||
} else {
|
||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.Header.Set("Content-Type", ctype)
|
||||
}
|
||||
|
||||
req.Header.Set("Accept", "application/json")
|
||||
|
||||
// var resp *http.Response
|
||||
// var err error
|
||||
buf, _ := io.ReadAll(req.Body)
|
||||
|
||||
// for i := uint64(0); i < ldc.cfg.Retries; i++ {
|
||||
// req = req.Clone(req.Context())
|
||||
req.Body = io.NopCloser(bytes.NewBuffer(buf))
|
||||
resp, err := ldc.client.Do(req)
|
||||
if err != nil || resp == nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
if resp.StatusCode == 200 {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
respBytes, err := io.ReadAll(resp.Body)
|
||||
buf, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Body.Close()
|
||||
return resp, errors.New(string(respBytes))
|
||||
}
|
||||
|
||||
func createK8sCloudApiLegacy(req k8s_ca.CreateRequest, token string) (*bytes.Buffer, string) {
|
||||
reqBody := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(reqBody)
|
||||
if req.OidcCertificate != "" {
|
||||
part, _ := writer.CreateFormFile("oidcCertificate", "ca.crt")
|
||||
_, _ = io.Copy(part, strings.NewReader(req.OidcCertificate))
|
||||
}
|
||||
|
||||
_ = writer.WriteField("name", req.Name)
|
||||
_ = writer.WriteField("rgId", strconv.FormatUint(req.RGID, 10))
|
||||
_ = writer.WriteField("k8ciId", strconv.FormatUint(req.K8SCIID, 10))
|
||||
_ = writer.WriteField("workerGroupName", req.WorkerGroupName)
|
||||
_ = writer.WriteField("networkPlugin", req.NetworkPlugin)
|
||||
|
||||
if req.MasterSEPID != 0 {
|
||||
_ = writer.WriteField("masterSepId", strconv.FormatUint(req.MasterSEPID, 10))
|
||||
}
|
||||
if req.MasterSEPPool != "" {
|
||||
_ = writer.WriteField("masterSepPool", req.MasterSEPPool)
|
||||
}
|
||||
if req.WorkerSEPID != 0 {
|
||||
_ = writer.WriteField("workerSepId", strconv.FormatUint(req.WorkerSEPID, 10))
|
||||
}
|
||||
if req.WorkerSEPPool != "" {
|
||||
_ = writer.WriteField("workerSepPool", req.WorkerSEPPool)
|
||||
}
|
||||
|
||||
if req.Labels != nil {
|
||||
for _, v := range req.Labels {
|
||||
_ = writer.WriteField("labels", v)
|
||||
}
|
||||
}
|
||||
if req.Taints != nil {
|
||||
for _, v := range req.Taints {
|
||||
_ = writer.WriteField("taints", v)
|
||||
}
|
||||
}
|
||||
if req.Annotations != nil {
|
||||
for _, v := range req.Annotations {
|
||||
_ = writer.WriteField("annotations", v)
|
||||
}
|
||||
}
|
||||
|
||||
if req.MasterCPU != 0 {
|
||||
_ = writer.WriteField("masterCpu", strconv.FormatUint(uint64(req.MasterCPU), 10))
|
||||
}
|
||||
if req.MasterNum != 0 {
|
||||
_ = writer.WriteField("masterNum", strconv.FormatUint(uint64(req.MasterNum), 10))
|
||||
}
|
||||
if req.MasterRAM != 0 {
|
||||
_ = writer.WriteField("masterRam", strconv.FormatUint(uint64(req.MasterRAM), 10))
|
||||
}
|
||||
if req.MasterDisk != 0 {
|
||||
_ = writer.WriteField("masterDisk", strconv.FormatUint(uint64(req.MasterDisk), 10))
|
||||
}
|
||||
if req.WorkerCPU != 0 {
|
||||
_ = writer.WriteField("workerCpu", strconv.FormatUint(uint64(req.WorkerCPU), 10))
|
||||
}
|
||||
if req.WorkerNum != 0 {
|
||||
_ = writer.WriteField("workerNum", strconv.FormatUint(uint64(req.WorkerNum), 10))
|
||||
}
|
||||
if req.WorkerRAM != 0 {
|
||||
_ = writer.WriteField("workerRam", strconv.FormatUint(uint64(req.WorkerRAM), 10))
|
||||
}
|
||||
if req.WorkerDisk != 0 {
|
||||
_ = writer.WriteField("workerDisk", strconv.FormatUint(uint64(req.WorkerDisk), 10))
|
||||
}
|
||||
if req.ExtNetID != 0 {
|
||||
_ = writer.WriteField("extnetId", strconv.FormatUint(req.ExtNetID, 10))
|
||||
}
|
||||
if req.VinsId != 0 {
|
||||
_ = writer.WriteField("vinsId", strconv.FormatUint(req.VinsId, 10))
|
||||
}
|
||||
if !req.WithLB {
|
||||
_ = writer.WriteField("withLB", strconv.FormatBool(req.WithLB))
|
||||
}
|
||||
|
||||
_ = writer.WriteField("highlyAvailableLB", strconv.FormatBool(req.HighlyAvailable))
|
||||
|
||||
if req.AdditionalSANs != nil {
|
||||
for _, v := range req.AdditionalSANs {
|
||||
_ = writer.WriteField("additionalSANs", v)
|
||||
}
|
||||
}
|
||||
if req.InitConfiguration != "" {
|
||||
_ = writer.WriteField("initConfiguration", req.InitConfiguration)
|
||||
}
|
||||
if req.ClusterConfiguration != "" {
|
||||
_ = writer.WriteField("clusterConfiguration", req.ClusterConfiguration)
|
||||
}
|
||||
if req.KubeletConfiguration != "" {
|
||||
_ = writer.WriteField("kubeletConfiguration", req.KubeletConfiguration)
|
||||
}
|
||||
if req.KubeProxyConfiguration != "" {
|
||||
_ = writer.WriteField("kubeProxyConfiguration", req.KubeProxyConfiguration)
|
||||
}
|
||||
if req.JoinConfiguration != "" {
|
||||
_ = writer.WriteField("joinConfiguration", req.JoinConfiguration)
|
||||
}
|
||||
if req.Description != "" {
|
||||
_ = writer.WriteField("desc", req.Description)
|
||||
}
|
||||
if req.UserData != "" {
|
||||
_ = writer.WriteField("userData", req.UserData)
|
||||
}
|
||||
|
||||
_ = writer.WriteField("extnetOnly", strconv.FormatBool(req.ExtNetOnly))
|
||||
|
||||
_ = writer.WriteField("authkey", token)
|
||||
|
||||
ct := writer.FormDataContentType()
|
||||
writer.Close()
|
||||
|
||||
return reqBody, ct
|
||||
}
|
||||
|
||||
func createK8sCloudBrokerLegacy(req k8s_cb.CreateRequest, token string) (*bytes.Buffer, string) {
|
||||
reqBody := &bytes.Buffer{}
|
||||
writer := multipart.NewWriter(reqBody)
|
||||
if req.OidcCertificate != "" {
|
||||
part, _ := writer.CreateFormFile("oidcCertificate", "ca.crt")
|
||||
_, _ = io.Copy(part, strings.NewReader(req.OidcCertificate))
|
||||
}
|
||||
|
||||
_ = writer.WriteField("name", req.Name)
|
||||
_ = writer.WriteField("rgId", strconv.FormatUint(req.RGID, 10))
|
||||
_ = writer.WriteField("k8ciId", strconv.FormatUint(req.K8CIID, 10))
|
||||
_ = writer.WriteField("workerGroupName", req.WorkerGroupName)
|
||||
_ = writer.WriteField("networkPlugin", req.NetworkPlugin)
|
||||
|
||||
if req.MasterSEPID != 0 {
|
||||
_ = writer.WriteField("masterSepId", strconv.FormatUint(req.MasterSEPID, 10))
|
||||
}
|
||||
if req.MasterSEPPool != "" {
|
||||
_ = writer.WriteField("masterSepPool", req.MasterSEPPool)
|
||||
}
|
||||
if req.WorkerSEPID != 0 {
|
||||
_ = writer.WriteField("workerSepId", strconv.FormatUint(req.WorkerSEPID, 10))
|
||||
}
|
||||
if req.WorkerSEPPool != "" {
|
||||
_ = writer.WriteField("workerSepPool", req.WorkerSEPPool)
|
||||
}
|
||||
|
||||
if req.Labels != nil {
|
||||
for _, v := range req.Labels {
|
||||
_ = writer.WriteField("labels", v)
|
||||
}
|
||||
}
|
||||
if req.Taints != nil {
|
||||
for _, v := range req.Taints {
|
||||
_ = writer.WriteField("taints", v)
|
||||
}
|
||||
}
|
||||
if req.Annotations != nil {
|
||||
for _, v := range req.Annotations {
|
||||
_ = writer.WriteField("annotations", v)
|
||||
}
|
||||
}
|
||||
|
||||
if req.MasterCPU != 0 {
|
||||
_ = writer.WriteField("masterCpu", strconv.FormatUint(req.MasterCPU, 10))
|
||||
}
|
||||
if req.MasterNum != 0 {
|
||||
_ = writer.WriteField("masterNum", strconv.FormatUint(req.MasterNum, 10))
|
||||
}
|
||||
if req.MasterRAM != 0 {
|
||||
_ = writer.WriteField("masterRam", strconv.FormatUint(req.MasterRAM, 10))
|
||||
}
|
||||
if req.MasterDisk != 0 {
|
||||
_ = writer.WriteField("masterDisk", strconv.FormatUint(req.MasterDisk, 10))
|
||||
}
|
||||
if req.WorkerCPU != 0 {
|
||||
_ = writer.WriteField("workerCpu", strconv.FormatUint(req.WorkerCPU, 10))
|
||||
}
|
||||
if req.WorkerNum != 0 {
|
||||
_ = writer.WriteField("workerNum", strconv.FormatUint(req.WorkerNum, 10))
|
||||
}
|
||||
if req.WorkerRAM != 0 {
|
||||
_ = writer.WriteField("workerRam", strconv.FormatUint(req.WorkerRAM, 10))
|
||||
}
|
||||
if req.WorkerDisk != 0 {
|
||||
_ = writer.WriteField("workerDisk", strconv.FormatUint(req.WorkerDisk, 10))
|
||||
}
|
||||
if req.ExtNetID != 0 {
|
||||
_ = writer.WriteField("extnetId", strconv.FormatUint(req.ExtNetID, 10))
|
||||
}
|
||||
if req.VinsId != 0 {
|
||||
_ = writer.WriteField("vinsId", strconv.FormatUint(req.VinsId, 10))
|
||||
}
|
||||
if !req.WithLB {
|
||||
_ = writer.WriteField("withLB", strconv.FormatBool(req.WithLB))
|
||||
}
|
||||
|
||||
_ = writer.WriteField("highlyAvailableLB", strconv.FormatBool(req.HighlyAvailable))
|
||||
|
||||
if req.AdditionalSANs != nil {
|
||||
for _, v := range req.AdditionalSANs {
|
||||
_ = writer.WriteField("additionalSANs", v)
|
||||
}
|
||||
}
|
||||
if req.InitConfiguration != "" {
|
||||
_ = writer.WriteField("initConfiguration", req.InitConfiguration)
|
||||
}
|
||||
if req.ClusterConfiguration != "" {
|
||||
_ = writer.WriteField("clusterConfiguration", req.ClusterConfiguration)
|
||||
}
|
||||
if req.KubeletConfiguration != "" {
|
||||
_ = writer.WriteField("kubeletConfiguration", req.KubeletConfiguration)
|
||||
}
|
||||
if req.KubeProxyConfiguration != "" {
|
||||
_ = writer.WriteField("kubeProxyConfiguration", req.KubeProxyConfiguration)
|
||||
}
|
||||
if req.JoinConfiguration != "" {
|
||||
_ = writer.WriteField("joinConfiguration", req.JoinConfiguration)
|
||||
}
|
||||
if req.Description != "" {
|
||||
_ = writer.WriteField("desc", req.Description)
|
||||
}
|
||||
if req.UserData != "" {
|
||||
_ = writer.WriteField("userData", req.UserData)
|
||||
}
|
||||
|
||||
_ = writer.WriteField("extnetOnly", strconv.FormatBool(req.ExtNetOnly))
|
||||
|
||||
_ = writer.WriteField("authkey", token)
|
||||
|
||||
ct := writer.FormDataContentType()
|
||||
|
||||
writer.Close()
|
||||
return reqBody, ct
|
||||
|
||||
req.Body.Close()
|
||||
req.Body = io.NopCloser(bytes.NewBuffer(buf))
|
||||
|
||||
resp, err := ldc.client.Do(req)
|
||||
if resp != nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
|
||||
// retries logic GOES HERE
|
||||
// get http response
|
||||
//var resp *http.Response
|
||||
//for i := uint64(0); i < ldc.cfg.Retries; i++ {
|
||||
// req := req.Clone(req.Context())
|
||||
// req.Body = io.NopCloser(bytes.NewBuffer(buf))
|
||||
//
|
||||
// if i > 0 {
|
||||
// time.Sleep(5 * time.Second) // no time sleep for the first request
|
||||
// }
|
||||
//
|
||||
// resp, err = ldc.client.Do(req)
|
||||
//
|
||||
// // stop retries on success and close response body
|
||||
// if resp != nil {
|
||||
// defer resp.Body.Close()
|
||||
// }
|
||||
// if err == nil {
|
||||
// break
|
||||
// }
|
||||
//
|
||||
// // retries in case of connection errors with time sleep
|
||||
// if isConnectionError(err) {
|
||||
// continue
|
||||
// }
|
||||
//
|
||||
// // return error in case of non-connection error
|
||||
// return nil, err
|
||||
//}
|
||||
|
||||
// handle http request errors
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp == nil {
|
||||
return nil, fmt.Errorf("got empty response without error")
|
||||
}
|
||||
|
||||
// handle successful request
|
||||
respBytes, _ := io.ReadAll(resp.Body)
|
||||
if resp.StatusCode == 200 {
|
||||
return respBytes, nil
|
||||
}
|
||||
|
||||
// handle errors with status code other than 200
|
||||
err = fmt.Errorf("%s", respBytes)
|
||||
return nil, fmt.Errorf("could not execute request: %w", err)
|
||||
}
|
||||
|
||||
@@ -38,8 +38,8 @@ type AffinityRuleAddRequest struct {
|
||||
Key string `url:"key" json:"key" validate:"required"`
|
||||
|
||||
// Value that must match the key to be taken into account when analyzing this rule
|
||||
// Required: true
|
||||
Value string `url:"value" json:"value" validate:"required"`
|
||||
// Required: false
|
||||
Value string `url:"value" json:"value"`
|
||||
}
|
||||
|
||||
// AffinityRuleAdd add affinity rule
|
||||
|
||||
@@ -38,8 +38,8 @@ type AffinityRuleRemoveRequest struct {
|
||||
Key string `url:"key" json:"key" validate:"required"`
|
||||
|
||||
// Value that must match the key to be taken into account when analyzing this rule
|
||||
// Required: true
|
||||
Value string `url:"value" json:"value" validate:"required"`
|
||||
// Required: false
|
||||
Value string `url:"value" json:"value"`
|
||||
}
|
||||
|
||||
// AffinityRuleRemove remove affinity rule
|
||||
|
||||
@@ -38,8 +38,8 @@ type AntiAffinityRuleAddRequest struct {
|
||||
Key string `url:"key" json:"key" validate:"required"`
|
||||
|
||||
// Value that must match the key to be taken into account when analyzing this rule
|
||||
// Required: true
|
||||
Value string `url:"value" json:"value" validate:"required"`
|
||||
// Required: false
|
||||
Value string `url:"value" json:"value"`
|
||||
}
|
||||
|
||||
// AntiAffinityRuleAdd add anti affinity rule
|
||||
|
||||
@@ -38,8 +38,8 @@ type AntiAffinityRuleRemoveRequest struct {
|
||||
Key string `url:"key" json:"key" validate:"required"`
|
||||
|
||||
// Value that must match the key to be taken into account when analyzing this rule
|
||||
// Required: true
|
||||
Value string `url:"value" json:"value" validate:"required"`
|
||||
// Required: false
|
||||
Value string `url:"value" json:"value"`
|
||||
}
|
||||
|
||||
// AntiAffinityRuleRemove remove anti affinity rule
|
||||
|
||||
@@ -202,7 +202,7 @@ func (k8s K8S) Create(ctx context.Context, req CreateRequest) (string, error) {
|
||||
|
||||
url := "/cloudapi/k8s/create"
|
||||
|
||||
res, err := k8s.client.DecortApiCall(ctx, http.MethodPost, url, req)
|
||||
res, err := k8s.client.DecortApiCallMP(ctx, http.MethodPost, url, req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@ package locations
|
||||
|
||||
// Main information about locations
|
||||
type ItemLocation struct {
|
||||
// AuthBroker
|
||||
AuthBroker []string `json:"authBroker"`
|
||||
|
||||
// Grid ID
|
||||
GID uint64 `json:"gid"`
|
||||
|
||||
|
||||
@@ -486,6 +486,9 @@ type RecordLoadBalancer struct {
|
||||
// Access Control List
|
||||
ACL interface{} `json:"acl"`
|
||||
|
||||
// BackendHAIP
|
||||
BackendHAIP string `json:"backendHAIP"`
|
||||
|
||||
// List of Backends
|
||||
Backends ListBackends `json:"backends"`
|
||||
|
||||
@@ -510,6 +513,9 @@ type RecordLoadBalancer struct {
|
||||
// External network ID
|
||||
ExtNetID uint64 `json:"extnetId"`
|
||||
|
||||
// FrontendHAIP
|
||||
FrontendHAIP string `json:"frontendHAIP"`
|
||||
|
||||
// List of frontends
|
||||
Frontends ListFrontends `json:"frontends"`
|
||||
|
||||
|
||||
@@ -40,8 +40,8 @@ type AffinityRuleAddRequest struct {
|
||||
Key string `url:"key" json:"key" validate:"required"`
|
||||
|
||||
// Value that must match the key to be taken into account when analyzing this rule
|
||||
// Required: true
|
||||
Value string `url:"value" json:"value" validate:"required"`
|
||||
// Required: false
|
||||
Value string `url:"value" json:"value"`
|
||||
}
|
||||
|
||||
// AffinityRuleAdd adds affinity rule
|
||||
|
||||
@@ -38,8 +38,8 @@ type AffinityRuleRemoveRequest struct {
|
||||
Key string `url:"key" json:"key" validate:"required"`
|
||||
|
||||
// Value that must match the key to be taken into account when analyzing this rule
|
||||
// Required: true
|
||||
Value string `url:"value" json:"value" validate:"required"`
|
||||
// Required: false
|
||||
Value string `url:"value" json:"value"`
|
||||
}
|
||||
|
||||
// AffinityRuleRemove remove affinity rule
|
||||
|
||||
@@ -38,8 +38,8 @@ type AntiAffinityRuleAddRequest struct {
|
||||
Key string `url:"key" json:"key" validate:"required"`
|
||||
|
||||
// Value that must match the key to be taken into account when analyzing this rule
|
||||
// Required: true
|
||||
Value string `url:"value" json:"value" validate:"required"`
|
||||
// Required: false
|
||||
Value string `url:"value" json:"value"`
|
||||
}
|
||||
|
||||
// AntiAffinityRuleAdd adds anti affinity rule
|
||||
|
||||
@@ -38,8 +38,8 @@ type AntiAffinityRuleRemoveRequest struct {
|
||||
Key string `url:"key" json:"key" validate:"required"`
|
||||
|
||||
// Value that must match the key to be taken into account when analyzing this rule
|
||||
// Required: true
|
||||
Value string `url:"value" json:"value" validate:"required"`
|
||||
// Required: false
|
||||
Value string `url:"value" json:"value"`
|
||||
}
|
||||
|
||||
// AntiAffinityRuleRemove removes anti affinity rule
|
||||
|
||||
@@ -204,7 +204,7 @@ func (k K8S) Create(ctx context.Context, req CreateRequest) (string, error) {
|
||||
|
||||
url := "/cloudbroker/k8s/create"
|
||||
|
||||
res, err := k.client.DecortApiCall(ctx, http.MethodPost, url, req)
|
||||
res, err := k.client.DecortApiCallMP(ctx, http.MethodPost, url, req)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -607,6 +607,9 @@ type ItemLB struct {
|
||||
// List ACL
|
||||
ACL ListACL `json:"acl"`
|
||||
|
||||
// BackendHAIP
|
||||
BackendHAIP string `json:"backendHAIP"`
|
||||
|
||||
// List backends
|
||||
Backends ListBackends `json:"backends"`
|
||||
|
||||
@@ -631,6 +634,9 @@ type ItemLB struct {
|
||||
// External network ID
|
||||
ExtNetID uint64 `json:"extnetId"`
|
||||
|
||||
// FrontendHAIP
|
||||
FrontendHAIP string `json:"frontendHAIP"`
|
||||
|
||||
// List of frontends
|
||||
Frontends ListFrontends `json:"frontends"`
|
||||
|
||||
|
||||
Reference in New Issue
Block a user