docs: пометить отменённый заход модификаторов как LEGACY + исправить ложные факты
- баннеры «ЛОЖНЫЙ ПУТЬ — ОТМЕНЕНО» на 4 файла HISTORY/OPUS/2026-09-22_modifier_* и docs/60_strategy/modifier_resources_ideology_and_specification.md - vIPConfigure: replace-семантика, НЕ накопительная (по тесту docs/ORG_IP_MODIFIER_TEST_2026-09-22.md) - обновлены ссылки на перенесённые материалы (docs/... -> NOTES/..., HOW_TO/...)
This commit is contained in:
@@ -0,0 +1,373 @@
|
||||
<!-- ⛔ LEGACY: deck-api.ngcloud.ru ЗАКРЫВАЕТСЯ. Актуальный API: lk-api-gateway.ngcloud.ru/api/v1/svc -->
|
||||
# Матрица состояний ресурсов в облаке ngcloud
|
||||
|
||||
## Основные параметры состояния
|
||||
|
||||
### 1. В структуре инстанса (instance object)
|
||||
|
||||
Каждый ресурс имеет следующие флаги:
|
||||
|
||||
| Параметр | Тип | Возможные значения | Описание |
|
||||
|----------|-----|------------------|---------|
|
||||
| `explainedStatus` | string | `running`, (другие?) | Основной объяснительный статус |
|
||||
| `isCreated` | boolean | `true`, `false` | Был ли инстанс создан |
|
||||
| `isDeleted` | boolean | `true`, `false` | Отмечен ли как удаленный |
|
||||
| `isSuspended` | boolean | `true`, `false` | Приостановлен ли |
|
||||
| `operationIsInProgress` | boolean | `true`, `false` | Идет ли корректная операция |
|
||||
| `operationIsPending` | boolean | `true`, `false` | Есть ли ожидающие операции |
|
||||
| `uptime` | number | >= 0 | Время работы в секундах (0, если не создан) |
|
||||
|
||||
### 2. Структура state (instanceState object)
|
||||
|
||||
```json
|
||||
{
|
||||
"state": {
|
||||
"instanceStateUid": "uuid",
|
||||
"version": 1, // версия состояния
|
||||
"dtState": "timestamp", // дата состояния
|
||||
"isTest": boolean
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Операции (operations array)
|
||||
|
||||
```json
|
||||
{
|
||||
"operation": "create|modify|delete|suspend|resume",
|
||||
"dtSubmit": "timestamp",
|
||||
"dtStart": null, // null если еще не началась
|
||||
"dtFinish": null, // null если еще не закончилась
|
||||
"isSuccessful": null|true|false, // null если не выполнялась
|
||||
"isInProgress": boolean,
|
||||
"isPending": boolean,
|
||||
"submitResult": "201|..." // HTTP статус результата отправки
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## МАТРИЦА СОСТОЯНИЙ
|
||||
|
||||
### ✅ СОСТОЯНИЕ: АКТИВНЫЙ РАБОЧИЙ ИНСТАНС (RUNNING)
|
||||
```
|
||||
isCreated=true
|
||||
isDeleted=false
|
||||
isSuspended=false
|
||||
explainedStatus="running"
|
||||
operationIsInProgress=false
|
||||
operationIsPending=false
|
||||
uptime > 0
|
||||
state.version >= 1
|
||||
```
|
||||
**Значение:** Ресурс создан, работает, нет выполняющихся операций
|
||||
|
||||
---
|
||||
|
||||
### 🔄 СОСТОЯНИЕ: СОЗДАНИЕ В ПРОЦЕССЕ (CREATING)
|
||||
```
|
||||
isCreated=true (или может быть false на ранних стадиях)
|
||||
isDeleted=false
|
||||
isSuspended=false
|
||||
operationIsInProgress=true
|
||||
operationIsPending=false
|
||||
state.version = 1
|
||||
operations[0].operation = "create"
|
||||
operations[0].isSuccessful = null (еще не завершено)
|
||||
operations[0].dtStart != null
|
||||
operations[0].dtFinish = null
|
||||
```
|
||||
**Значение:** Операция создания выполняется
|
||||
|
||||
---
|
||||
|
||||
### ⏸️ СОСТОЯНИЕ: ПРИОСТАНОВЛЕН (SUSPENDED)
|
||||
```
|
||||
isCreated=true
|
||||
isDeleted=false
|
||||
isSuspended=true
|
||||
explainedStatus="suspended" (или может быть другое)
|
||||
operationIsInProgress=false
|
||||
```
|
||||
**Значение:** Ресурс создан, но приостановлен (власт нет - suspend)
|
||||
|
||||
---
|
||||
|
||||
### ⏸️ СОСТОЯНИЕ: ПРИОСТАНОВКА В ПРОЦЕССЕ (SUSPENDING)
|
||||
```
|
||||
isCreated=true
|
||||
isSuspended=false (еще не приостановлен)
|
||||
operationIsInProgress=true
|
||||
operations[last].operation = "suspend"
|
||||
operations[last].isSuccessful = null
|
||||
```
|
||||
**Значение:** Операция приостановки выполняется
|
||||
|
||||
---
|
||||
|
||||
### ▶️ СОСТОЯНИЕ: ВОЗОБНОВЛЕНИЕ В ПРОЦЕССЕ (RESUMING)
|
||||
```
|
||||
isCreated=true
|
||||
isSuspended=true (еще приостановлен)
|
||||
operationIsInProgress=true
|
||||
operations[last].operation = "resume"
|
||||
operations[last].isSuccessful = null
|
||||
```
|
||||
**Значение:** Операция возобновления выполняется
|
||||
|
||||
---
|
||||
|
||||
### ❌ СОСТОЯНИЕ: НЕ СОЗДАН (NOT CREATED)
|
||||
```
|
||||
isCreated=false
|
||||
isDeleted=false
|
||||
isSuspended=false
|
||||
state = null или пуст
|
||||
uptime = 0
|
||||
version = null или отсутствует
|
||||
```
|
||||
**Значение:** Ресурс был определен в коде, но никогда не создавался в облаке
|
||||
|
||||
---
|
||||
|
||||
### 🗑️ СОСТОЯНИЕ: УДАЛЕН (DELETED)
|
||||
```
|
||||
isDeleted=true
|
||||
isCreated=true (был когда-то создан)
|
||||
state.version может быть или не быть
|
||||
```
|
||||
**Значение:** Ресурс был удален, может оставаться в истории
|
||||
|
||||
---
|
||||
|
||||
### 🔄 СОСТОЯНИЕ: УДАЛЕНИЕ В ПРОЦЕССЕ (DELETING)
|
||||
```
|
||||
isCreated=true
|
||||
isDeleted=false (еще не отмечен как удаленный)
|
||||
operationIsInProgress=true
|
||||
operations[last].operation = "delete"
|
||||
operations[last].isSuccessful = null
|
||||
```
|
||||
**Значение:** Операция удаления выполняется
|
||||
|
||||
---
|
||||
|
||||
### 🔧 СОСТОЯНИЕ: ИЗМЕНЕНИЕ В ПРОЦЕССЕ (MODIFYING)
|
||||
```
|
||||
isCreated=true
|
||||
isDeleted=false
|
||||
operationIsInProgress=true
|
||||
operations[last].operation = "modify"
|
||||
operations[last].isSuccessful = null
|
||||
```
|
||||
**Значение:** Конфигурация ресурса изменяется
|
||||
|
||||
---
|
||||
|
||||
### ⏳ СОСТОЯНИЕ: ОПЕРАЦИЯ ОЖИДАЕТ (PENDING)
|
||||
```
|
||||
operationIsPending=true
|
||||
operationIsInProgress=false
|
||||
operations[last].dtStart = null (еще не началась, но создана)
|
||||
operations[last].isSuccessful = null
|
||||
```
|
||||
**Значение:** Операция создана, но еще не началась (в очереди)
|
||||
|
||||
---
|
||||
|
||||
### ⚠️ СОСТОЯНИЕ: ОШИБКА ОПЕРАЦИИ (OPERATION_FAILED)
|
||||
```
|
||||
operations[last].isSuccessful=false
|
||||
operations[last].dtFinish != null
|
||||
operations[last].errorLog != null
|
||||
operationIsInProgress=false
|
||||
```
|
||||
**Значение:** Последняя операция завершилась с ошибкой
|
||||
|
||||
---
|
||||
|
||||
### 🔀 СОСТОЯНИЕ: ГИБРИДНОЕ/ПЕРЕХОДНОЕ (HYBRID)
|
||||
|
||||
**Противоречивые комбинации указывают на переходное состояние:**
|
||||
- `operationIsInProgress=true` + `operations[last].dtStart=null` → операция только создана, еще не началась
|
||||
- `isCreated=true` + `operationIsPending=true` → есть ожидающая операция
|
||||
- `state.version=null` + `isCreated=true` → некорректное состояние
|
||||
|
||||
---
|
||||
|
||||
## АЛГОРИТМ ОПРЕДЕЛЕНИЯ СОСТОЯНИЯ
|
||||
|
||||
### На уровне API (GET /api/v1/index.cfm/instances/{uid})
|
||||
|
||||
```python
|
||||
def get_instance_state(instance_data):
|
||||
"""
|
||||
instance_data = {
|
||||
'isCreated': bool,
|
||||
'isDeleted': bool,
|
||||
'isSuspended': bool,
|
||||
'explainedStatus': str,
|
||||
'operationIsInProgress': bool,
|
||||
'operationIsPending': bool,
|
||||
'uptime': float,
|
||||
'state': {...} or null,
|
||||
'operations': [...]
|
||||
}
|
||||
"""
|
||||
|
||||
# Шаг 1: Проверка удаления
|
||||
if instance_data['isDeleted']:
|
||||
return 'DELETED'
|
||||
|
||||
# Шаг 2: Проверка создания
|
||||
if not instance_data['isCreated']:
|
||||
return 'NOT_CREATED'
|
||||
|
||||
# Шаг 3: Проверка операции в процессе
|
||||
if instance_data['operationIsInProgress']:
|
||||
last_op = instance_data['operations'][-1] if instance_data['operations'] else None
|
||||
if last_op:
|
||||
operation_type = last_op.get('operation', 'unknown')
|
||||
return f'{operation_type.upper()}_IN_PROGRESS'
|
||||
|
||||
# Шаг 4: Проверка ожидающей операции
|
||||
if instance_data['operationIsPending']:
|
||||
last_op = instance_data['operations'][-1] if instance_data['operations'] else None
|
||||
if last_op and not last_op.get('dtStart'):
|
||||
return 'OPERATION_PENDING'
|
||||
|
||||
# Шаг 5: Проверка приостановки
|
||||
if instance_data['isSuspended']:
|
||||
return 'SUSPENDED'
|
||||
|
||||
# Шаг 6: Проверка основного статуса
|
||||
if instance_data['explainedStatus'] == 'running':
|
||||
return 'RUNNING'
|
||||
|
||||
# Шаг 7: Проверка ошибок в операциях
|
||||
if instance_data['operations']:
|
||||
last_op = instance_data['operations'][-1]
|
||||
if last_op.get('isSuccessful') == False:
|
||||
return 'OPERATION_FAILED'
|
||||
|
||||
return 'UNKNOWN'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## РЕКОМЕНДУЕМЫЕ ЗАПРОСЫ К API
|
||||
|
||||
### Получить список ресурсов определенного типа
|
||||
|
||||
```bash
|
||||
GET /api/v1/index.cfm/instances?fields=instanceConfigDtCreated,instanceUid,displayName,svc,uptime,explainedStatus,svcExtendedName,updaterLogin,updaterShortname,operationIsInProgress,operationIsPending,monitoringUrl&search={resource_name}&isAuxiliary=false&isDeleted=false
|
||||
```
|
||||
|
||||
### Получить полную информацию о ресурсе
|
||||
|
||||
```bash
|
||||
GET /api/v1/index.cfm/instances/{instance_uid}?fields=instanceConfigDtCreated,instanceUid,displayName,descr,svc,state,operations,availableOperations,uptime,isDeleted,updaterLogin,updaterShortname,explainedStatus,man,dependencies,dependentInstances,svcExtendedName
|
||||
```
|
||||
|
||||
### Выполнить операцию
|
||||
|
||||
```bash
|
||||
POST /api/v1/index.cfm/instanceOperations
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"instanceUid": "uuid",
|
||||
"operation": "create|modify|delete|suspend|resume"
|
||||
}
|
||||
```
|
||||
|
||||
### Получить статус операции
|
||||
|
||||
```bash
|
||||
GET /api/v1/index.cfm/instanceOperations/{operation_uid}?fields=instanceOperationUid,instanceUid,state,stages,cfsParams,isSuccessful,dtCreated,dtUpdated,dtStart,dtFinish,operation,svcOperationId,svc,displayName,submitResult,duration,errorLog,updaterShortname,man,nestedRefData,isInProgress,isPending,dtSubmit
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ПРИМЕРЫ ИЗ HAR
|
||||
|
||||
### Пример 1: RUNNING инстанс
|
||||
```json
|
||||
{
|
||||
"displayName": "Newly Added",
|
||||
"isCreated": true,
|
||||
"isDeleted": false,
|
||||
"isSuspended": false,
|
||||
"explainedStatus": "running",
|
||||
"operationIsInProgress": false,
|
||||
"operationIsPending": false,
|
||||
"uptime": 1102.739073,
|
||||
"state": {
|
||||
"instanceStateUid": "48bfdbc5-e8fc-4286-8098-d6ab7f6de50e",
|
||||
"version": 2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Пример 2: CREATE_IN_PROGRESS инстанс
|
||||
```json
|
||||
{
|
||||
"displayName": "ttst00",
|
||||
"isCreated": true,
|
||||
"isDeleted": false,
|
||||
"isSuspended": false,
|
||||
"explainedStatus": "running",
|
||||
"operationIsInProgress": false,
|
||||
"operationIsPending": true,
|
||||
"uptime": 0,
|
||||
"operations": [
|
||||
{
|
||||
"operation": "modify",
|
||||
"dtSubmit": "2026-01-22T19:30:21.790+0300",
|
||||
"dtStart": null,
|
||||
"dtFinish": null,
|
||||
"isSuccessful": null,
|
||||
"isInProgress": false,
|
||||
"isPending": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Пример 3: DELETE_IN_PROGRESS инстанс
|
||||
```json
|
||||
{
|
||||
"displayName": "ttt0-terraform",
|
||||
"isCreated": true,
|
||||
"isDeleted": false,
|
||||
"isSuspended": false,
|
||||
"operations": [
|
||||
{
|
||||
"operation": "delete",
|
||||
"dtSubmit": "2026-01-22T19:28:24.720+0300",
|
||||
"dtStart": null,
|
||||
"dtFinish": null,
|
||||
"isSuccessful": null
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## СВОДНАЯ ТАБЛИЦА ВОЗМОЖНЫХ СОСТОЯНИЙ
|
||||
|
||||
| Состояние | isCreated | isDeleted | isSuspended | operationIsInProgress | Описание |
|
||||
|-----------|-----------|-----------|-------------|----------------------|---------|
|
||||
| NOT_CREATED | false | false | false | false | Еще не создан |
|
||||
| CREATING | true | false | false | true | Создание выполняется |
|
||||
| RUNNING | true | false | false | false | Работает нормально |
|
||||
| MODIFYING | true | false | false | true | Конфигурация изменяется |
|
||||
| SUSPENDING | true | false | false | true | Приостановка выполняется |
|
||||
| SUSPENDED | true | false | true | false | Приостановлен |
|
||||
| RESUMING | true | false | true | true | Возобновление выполняется |
|
||||
| DELETING | true | false | false | true | Удаление выполняется |
|
||||
| DELETED | true | true | - | - | Удален |
|
||||
| OPERATION_PENDING | true | false | - | false | Есть ожидающая операция |
|
||||
| OPERATION_FAILED | true | false | - | false | Операция завершилась ошибкой |
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
<!-- ⛔ LEGACY: deck-api.ngcloud.ru ЗАКРЫВАЕТСЯ. Актуальный API: lk-api-gateway.ngcloud.ru/api/v1/svc -->
|
||||
# Instance State Transitions - Матрица состояний инстансов ngcloud
|
||||
|
||||
Полное руководство по определению состояния инстансов через API ngcloud на основе комбинаций флагов и истории операций.
|
||||
|
||||
---
|
||||
|
||||
## 📊 Таблица состояний
|
||||
|
||||
| **Состояние** | `isCreated` | `isDeleted` | `isSuspended` | `operationIsInProgress` | `operationIsPending` | `isSuccessful` | Описание |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **NOT_CREATED** | `false` | `false` | `false` | `false` | `false` | - | Ресурс в TF, но в облаке его нет |
|
||||
| **CREATING** | `false` | `false` | `false` | `true` | `false` | - | Идет создание инстанса |
|
||||
| **CREATION_FAILED** | `false` | `false` | `false` | `false` | `false` | `false` | ❌ Попытка создания завершилась ошибкой |
|
||||
| **RUNNING** | `true` | `false` | `false` | `false` | `false` | `true` | ✅ Нормальное работающее состояние |
|
||||
| **RUNNING_PENDING** | `true` | `false` | `false` | `false` | `true` | - | ✅ Работает, но в очереди ждет операция |
|
||||
|
||||
---
|
||||
|
||||
## 📊 Полная таблица состояний
|
||||
|
||||
| **Состояние** | `isCreated` | `isDeleted` | `isSuspended` | `operationIsInProgress` | `operationIsPending` | `isSuccessful` | Описание |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| **NOT_CREATED** | `false` | `false` | `false` | `false` | `false` | - | Ресурс в TF, но в облаке его нет |
|
||||
| **CREATING** | `false` | `false` | `false` | `true` | `false` | - | Идет создание инстанса |
|
||||
| **CREATION_FAILED** | `false` | `false` | `false` | `false` | `false` | `false` | ❌ Попытка создания завершилась ошибкой |
|
||||
| **RUNNING** | `true` | `false` | `false` | `false` | `false` | `true` | ✅ Нормальное работающее состояние |
|
||||
| **RUNNING_PENDING** | `true` | `false` | `false` | `false` | `true` | - | ✅ Работает, но в очереди ждет операция |
|
||||
| **MODIFYING** | `true` | `false` | `false` | `true` | `false` | - | Идет изменение конфигурации |
|
||||
| **MODIFICATION_FAILED** | `true` | `false` | `false` | `false` | `false` | `false` | ❌ Ошибка при изменении |
|
||||
| **SUSPENDING** | `true` | `false` | `false` | `true` | `false` | - | Идет приостановка |
|
||||
| **SUSPEND_FAILED** | `true` | `false` | `false` | `false` | `false` | `false` | ❌ Ошибка при приостановке |
|
||||
| **SUSPENDED** | `true` | `false` | `true` | `false` | `false` | `true` | ⏸️ Приостановлен |
|
||||
| **RESUMING** | `true` | `false` | `true` | `true` | `false` | - | Идет возобновление |
|
||||
| **RESUME_FAILED** | `true` | `false` | `true` | `false` | `false` | `false` | ❌ Ошибка при возобновлении |
|
||||
| **DELETING** | `true` | `false` | `false` | `true` | `false` | - | Идет удаление |
|
||||
| **DELETION_FAILED** | `true` | `false` | `false` | `false` | `false` | `false` | ❌ Ошибка при удалении |
|
||||
| **DELETED** | `true` | `true` | - | - | - | - | ❌ Удален |
|
||||
| **ORPHANED** | `false` | `false` | - | - | - | `false` | ⚠️ История ошибок |
|
||||
|
||||
---
|
||||
|
||||
## 🔗 API Запросы для определения состояния
|
||||
|
||||
### Основной запрос к инстансу
|
||||
|
||||
```bash
|
||||
curl -s -H "Authorization: Bearer $(cat /home/naeel/remote_dev/terraform/secrets/prod.token)" \
|
||||
"https://deck-api.ngcloud.ru/api/v1/index.cfm/instances/{instanceUid}?fields=isCreated,isDeleted,isSuspended,operationIsInProgress,operationIsPending,operations,availableOperations,explainedStatus,uptime"
|
||||
```
|
||||
|
||||
### Ответ API при CREATION_FAILED
|
||||
|
||||
```json
|
||||
{
|
||||
"instance": {
|
||||
"instanceUid": "vcOrg-2402",
|
||||
"displayName": "Организация в Cloud Director",
|
||||
"isCreated": false,
|
||||
"isDeleted": false,
|
||||
"isSuspended": false,
|
||||
"operationIsInProgress": false,
|
||||
"operationIsPending": false,
|
||||
"operations": [
|
||||
{
|
||||
"operation": "create",
|
||||
"isSuccessful": false,
|
||||
"errorLog": "Connection timeout to Cloud Director API",
|
||||
"dtFinish": "2026-02-24T18:51:30+0300",
|
||||
"duration": 80.5
|
||||
}
|
||||
],
|
||||
"availableOperations": [
|
||||
{"operation": "create"},
|
||||
{"operation": "delete"}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🐍 Python функция определения состояния
|
||||
|
||||
```python
|
||||
def get_instance_state(instance_data):
|
||||
"""Определить состояние инстанса на основе ответа API"""
|
||||
|
||||
ic = instance_data.get('isCreated', False)
|
||||
id = instance_data.get('isDeleted', False)
|
||||
is_susp = instance_data.get('isSuspended', False)
|
||||
is_in_prog = instance_data.get('operationIsInProgress', False)
|
||||
is_pend = instance_data.get('operationIsPending', False)
|
||||
ops = instance_data.get('operations', [])
|
||||
|
||||
# ПЕРВЫЙ ПРИОРИТЕТ: Проверить ошибки в последней операции
|
||||
if ops and len(ops) > 0:
|
||||
last_op = ops[0]
|
||||
if last_op.get('isSuccessful') == False: # явное False
|
||||
op_type = last_op.get('operation', 'unknown').upper()
|
||||
return {
|
||||
'state': f'{op_type}_FAILED',
|
||||
'error': last_op.get('errorLog'),
|
||||
'attempt_at': last_op.get('dtFinish'),
|
||||
'can_retry': True
|
||||
}
|
||||
|
||||
# Операции в процессе
|
||||
if is_in_prog:
|
||||
return {'state': 'OPERATING'}
|
||||
|
||||
# Ожидающие операции
|
||||
if is_pend:
|
||||
return {'state': 'RUNNING_PENDING'}
|
||||
|
||||
# Не создан
|
||||
if not ic:
|
||||
return {'state': 'NOT_CREATED'}
|
||||
|
||||
# Удален
|
||||
if id:
|
||||
return {'state': 'DELETED'}
|
||||
|
||||
# Приостановлен
|
||||
if is_susp:
|
||||
return {'state': 'SUSPENDED'}
|
||||
|
||||
# Работает
|
||||
return {
|
||||
'state': 'RUNNING',
|
||||
'uptime': instance_data.get('uptime')
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 Логика для Terraform Provider
|
||||
|
||||
### terraform apply
|
||||
|
||||
- **NOT_CREATED** → CreateInstance
|
||||
- **RUNNING** + config_changed → ModifyInstance
|
||||
- **CREATION_FAILED**, **MODIFICATION_FAILED** → Retry
|
||||
- **OPERATING_*** → Wait
|
||||
|
||||
### terraform destroy
|
||||
|
||||
- **NOT_CREATED** → Skip
|
||||
- **DELETED** → RemoveFromState
|
||||
- **DELETION_FAILED** → Retry
|
||||
- **Остальные** → DeleteInstance
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Критические комбинации
|
||||
|
||||
### 1. Инстанс не видно, но ошибка создания
|
||||
|
||||
```
|
||||
isCreated=false + operations[0].isSuccessful=false + operation=create
|
||||
= CREATION_FAILED
|
||||
```
|
||||
|
||||
**Действие:**
|
||||
- Проверить `errorLog` для деталей
|
||||
- Использовать `availableOperations` для определения возможных действий
|
||||
- Перепопытаться через create или удалить
|
||||
|
||||
### 2. Операция зависла
|
||||
|
||||
```
|
||||
operationIsInProgress=true + duration > 3600 сек
|
||||
= STUCK_OPERATION
|
||||
```
|
||||
|
||||
**Действие:** Требует ручного вмешательства
|
||||
|
||||
### 3. Ошибка удаления (инстанс остался)
|
||||
|
||||
```
|
||||
isDeleted=false + operations[0].operation=delete + isSuccessful=false
|
||||
= DELETION_FAILED
|
||||
```
|
||||
|
||||
**Действие:**
|
||||
- Инстанс остается в облаке
|
||||
- Перепопытаться delete или обратиться в поддержку
|
||||
|
||||
---
|
||||
|
||||
## 📖 Ссылки
|
||||
|
||||
- API: https://deck-api.ngcloud.ru/api/v1/
|
||||
- Операции: create, modify, delete, suspend, resume
|
||||
- Токен: /home/naeel/remote_dev/terraform/secrets/prod.token
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
# Universal Provider (ядро + генератор ресурсов) — подробный отчёт
|
||||
|
||||
Дата: 2026-01-31
|
||||
|
||||
## Цель
|
||||
Нужна архитектура «универсального провайдера», где:
|
||||
- есть **ядро** (общий клиент и универсальный флоу операций);
|
||||
- есть **папка ресурсов** (Go-файлы ресурсов);
|
||||
- есть **папка YAML-описаний сервисов** (чтобы DevOps мог добавить сервис одним файлом);
|
||||
- есть **генератор**, который:
|
||||
- читает YAML,
|
||||
- генерирует ресурсы,
|
||||
- генерирует реестр ресурсов,
|
||||
- дальше выполняется build (ядро + ресурсы из папки).
|
||||
|
||||
В итоге: если файла ресурса нет — в провайдере его не будет. Если YAML изменён — перегенерация.
|
||||
|
||||
---
|
||||
|
||||
## Итоговая архитектура (v2.0.0, «голое» ядро + ресурсы)
|
||||
Новая сборка размещена в:
|
||||
- [nubes_provider_gen](nubes_provider_gen)
|
||||
|
||||
Структура:
|
||||
- [nubes_provider_gen/internal/core](nubes_provider_gen/internal/core) — **ядро** (клиент, универсальный flow, запуск операций)
|
||||
- [nubes_provider_gen/internal/provider](nubes_provider_gen/internal/provider) — **провайдер** (schema + клиент)
|
||||
- [nubes_provider_gen/internal/resources_gen](nubes_provider_gen/internal/resources_gen) — **сгенерированные ресурсы** (Go)
|
||||
- [nubes_provider_gen/resources_yaml](nubes_provider_gen/resources_yaml) — **YAML-описания сервисов**
|
||||
- [nubes_provider_gen/tools/gen](nubes_provider_gen/tools/gen) — **генератор**
|
||||
- [nubes_provider_gen/test_persistent](nubes_provider_gen/test_persistent) — тестовые манифесты
|
||||
|
||||
### 1) Ядро (core)
|
||||
Файл: [nubes_provider_gen/internal/core/client.go](nubes_provider_gen/internal/core/client.go)
|
||||
|
||||
Содержит:
|
||||
- `UniversalClient` с HTTP-клиентом и API endpoint/token.
|
||||
- Универсальный flow **CreateGenericInstanceUniversalV6**:
|
||||
1. POST /instances
|
||||
2. POST /instanceOperations (create)
|
||||
3. GET /instanceOperations/{opUid}?fields=cfsParams
|
||||
4. POST /instanceOperationCfsParams (явные параметры)
|
||||
5. POST /instanceOperationCfsParams (дефолтные/пустые для пропущенных)
|
||||
6. GET /instanceOperations/{opUid}/validate-cfs
|
||||
7. POST /instanceOperations/{opUid}/run
|
||||
- Универсальные операции:
|
||||
- `RunInstanceOperationUniversal` (delete/suspend/resume и т.д.)
|
||||
- `RunInstanceOperationUniversalWithDefaults` (modify с автоподстановкой параметров)
|
||||
- Утилиты:
|
||||
- `FindInstanceByDisplayName`
|
||||
- `GetInstanceState`
|
||||
|
||||
**Почему нужно WithDefaults для modify**
|
||||
На modify требуются обязательные параметры (часто даже те, что не меняются). Без них backend падает.
|
||||
|
||||
### 2) Провайдер (provider)
|
||||
Файл: [nubes_provider_gen/internal/provider/provider.go](nubes_provider_gen/internal/provider/provider.go)
|
||||
|
||||
- Тип провайдера: `nubes`
|
||||
- Адрес: `terrareg.kube5s.ru/nubes/nubes`
|
||||
- Ресурсы приходят из **реестра**:
|
||||
- `resources_gen.AllResources()` — возвращает список функций создания ресурсов.
|
||||
|
||||
### 3) Генератор (tools/gen)
|
||||
Файл: [nubes_provider_gen/tools/gen/main.go](nubes_provider_gen/tools/gen/main.go)
|
||||
|
||||
Функции:
|
||||
- Читает все YAML-файлы в `resources_yaml/`
|
||||
- Генерирует ресурсный Go-файл в `internal/resources_gen/` для каждого YAML
|
||||
- Генерирует `registry.go` с `AllResources()`
|
||||
|
||||
То есть:
|
||||
- **Добавили YAML → сгенерировали Go → build**
|
||||
- Нет YAML → нет Go → нет ресурса
|
||||
|
||||
### 4) YAML-описания
|
||||
Файл: [nubes_provider_gen/resources_yaml/dummy.yaml](nubes_provider_gen/resources_yaml/dummy.yaml)
|
||||
|
||||
Минимальная схема (пример dummy):
|
||||
- `name`, `service_id`, `display_name_default`
|
||||
- `create.params` — ID параметров create
|
||||
- `modify.params` — ID параметров modify
|
||||
- `lifecycle` — defaults для `adopt_existing_on_create` и `suspend_on_destroy`
|
||||
|
||||
---
|
||||
|
||||
## Что было сделано (конкретные шаги)
|
||||
|
||||
### Шаг 1. «Голое» ядро + генератор
|
||||
Создан отдельный проект:
|
||||
- [nubes_provider_gen](nubes_provider_gen)
|
||||
Сделан core + provider + tools/gen + resources_yaml.
|
||||
|
||||
### Шаг 2. YAML для dummy
|
||||
Создан YAML dummy: [nubes_provider_gen/resources_yaml/dummy.yaml](nubes_provider_gen/resources_yaml/dummy.yaml)
|
||||
|
||||
### Шаг 3. Генерация ресурсов
|
||||
Команда:
|
||||
- `go run ./tools/gen`
|
||||
|
||||
Сгенерированы:
|
||||
- [nubes_provider_gen/internal/resources_gen/dummy_resource.go](nubes_provider_gen/internal/resources_gen/dummy_resource.go)
|
||||
- [nubes_provider_gen/internal/resources_gen/registry.go](nubes_provider_gen/internal/resources_gen/registry.go)
|
||||
|
||||
### Шаг 4. Build
|
||||
Команда:
|
||||
- `go build -o terraform-provider-nubes`
|
||||
|
||||
### Шаг 5. Тесты dummy
|
||||
Тестовый конфиг:
|
||||
- [nubes_provider_gen/test_persistent/main.tf](nubes_provider_gen/test_persistent/main.tf)
|
||||
- [nubes_provider_gen/test_persistent/dev_override.tfrc](nubes_provider_gen/test_persistent/dev_override.tfrc)
|
||||
|
||||
Проверены сценарии:
|
||||
1. `apply` (create/adopt) — OK
|
||||
2. `modify` (duration 750 → 820) — OK
|
||||
3. `destroy` (`suspend_on_destroy = true`) — OK
|
||||
4. `apply` после suspend (resume/adopt c явным `adopt_existing_on_create`) — OK
|
||||
5. удаление ресурса из манифеста + `apply` — OK
|
||||
6. возвращение ресурса в манифест + `apply` — OK
|
||||
|
||||
---
|
||||
|
||||
## Ошибки и как решались
|
||||
|
||||
### 1) `action modify not available`
|
||||
Причина: в Update использовался `id` из `Plan` (неизвестен).
|
||||
Решение: брать `id` из `State`.
|
||||
|
||||
### 2) `API error 400: Parameter specified does not belong to this operation`
|
||||
Причина: неверные ID параметров modify.
|
||||
Решение: для modify использовать `287/288` вместо `198/199`.
|
||||
|
||||
### 3) `API error 500: checkParam ... instanceOperationCfsParamUid` (повторно)
|
||||
Причина: операция modify требует подстановки всех параметров; без них backend падает.
|
||||
Решение:
|
||||
- Добавлен `RunInstanceOperationUniversalWithDefaults` — читает `cfsParams` и заполняет недостающие
|
||||
- Позже для dummy оказалось нужно отправлять **все** параметры (не только required)
|
||||
|
||||
### 4) `TLS handshake timeout`
|
||||
Причина: VPN/сеть.
|
||||
Решение: повторить команду.
|
||||
|
||||
### 5) `status 401`
|
||||
Причина: токен истёк/невалидный.
|
||||
Решение: заменить токен и сохранить по правилу `/home/naeel/terra/HH-MM-SS.token`.
|
||||
|
||||
### 6) Критерий завершения операции (важно)
|
||||
Любая операция (create/modify/delete/suspend/resume) считается **завершённой**, когда у неё заполнено **время окончания**.
|
||||
Это является признаком завершения **и успеха, и ошибки**. В UI и API ориентируемся на наличие `dtFinish`.
|
||||
|
||||
---
|
||||
|
||||
## Текущее состояние
|
||||
- Провайдер «голый» + ресурсы из YAML работает.
|
||||
- `dummy` полностью тестируется из YAML → Go → build.
|
||||
- В `resources_gen` присутствует 1 ресурс: `nubes_dummy`.
|
||||
|
||||
---
|
||||
|
||||
## План (следующий шаг)
|
||||
1) Расширить YAML-схему:
|
||||
- поддержку дополнительных параметров (например, failInProgress, whereFail и т.п.)
|
||||
- поддержку optional параметров с явными defaults
|
||||
2) Добавить генерацию документации из YAML
|
||||
3) Добавить проверку схемы YAML (валидация) перед генерацией
|
||||
4) Скрипт "build pipeline":
|
||||
- gen → registry → go build
|
||||
5) Опционально: тестовый фреймворк для «batch» тестов
|
||||
|
||||
---
|
||||
|
||||
## Важные файлы
|
||||
- Ядро: [nubes_provider_gen/internal/core/client.go](nubes_provider_gen/internal/core/client.go)
|
||||
- Генератор: [nubes_provider_gen/tools/gen/main.go](nubes_provider_gen/tools/gen/main.go)
|
||||
- Реестр: [nubes_provider_gen/internal/resources_gen/registry.go](nubes_provider_gen/internal/resources_gen/registry.go)
|
||||
- YAML dummy: [nubes_provider_gen/resources_yaml/dummy.yaml](nubes_provider_gen/resources_yaml/dummy.yaml)
|
||||
- Сгенерированный ресурс: [nubes_provider_gen/internal/resources_gen/dummy_resource.go](nubes_provider_gen/internal/resources_gen/dummy_resource.go)
|
||||
- Тесты: [nubes_provider_gen/test_persistent/main.tf](nubes_provider_gen/test_persistent/main.tf)
|
||||
|
||||
---
|
||||
|
||||
## MAYDO (отложено до запроса заказчика)
|
||||
- Добавить в YAML `outputs` (из `state/out`) и `state_params` (из `state/params`).
|
||||
- Добавить `param_meta`: `valueList`, `func`, `refSvcId`, `dataDescriptor`.
|
||||
- Добавить `secrets` (из `vault`) с пометкой чувствительных данных.
|
||||
- Добавить `operations` (man/описания, доступные операции) для генерации доков.
|
||||
- Добавить поддержку `subparams` (nested/list/json) и типизацию сложных структур.
|
||||
- Добавить обработку версий операций (если API это отдаёт).
|
||||
@@ -0,0 +1,14 @@
|
||||
# ⛔⛔⛔ LEGACY — СТАРЫЕ API ЗАКРЫВАЮТСЯ ⛔⛔⛔
|
||||
# НИКОГДА не использовать для генерации! Только для справки.
|
||||
# Актуальные API:
|
||||
# https://lk-api-gateway.ngcloud.ru/api/v1/svc (prod)
|
||||
# https://lk-api-gateway-dev.ngcloud.ru/api/v1/svc (dev)
|
||||
# https://lk-api-gateway-test.ngcloud.ru/api/v1/svc (test)
|
||||
#
|
||||
# === НИЖЕ — ЛЕГАСИ, НЕ ИСПОЛЬЗОВАТЬ ===
|
||||
|
||||
https://deck-api.ngcloud.ru/api/v1
|
||||
|
||||
https://deck-api-dev.ngcloud.ru/api/v1
|
||||
|
||||
https://deck-api-test.ngcloud.ru/api/v1
|
||||
Reference in New Issue
Block a user