Docs: полная схема CREATE + ошибки + хронология сессии 6

This commit is contained in:
2026-07-24 16:10:15 +04:00
parent 2f7d9d3d7b
commit d5ccf61f84
5 changed files with 32908 additions and 2 deletions
+266
View File
@@ -0,0 +1,266 @@
# Полный поток CREATE — рабочая схема (доказано curl 2026-07-24)
## Базовые константы
```bash
API="https://lk-api-gateway-test.ngcloud.ru/api/v1/svc"
TOKEN=$(tr -d '\n' < secrets/test.token)
UA="Mozilla/5.0"
CT="Content-Type: application/json"
```
## ⛔ Где работает curl, а где нет
| Источник | Работает? | Причина |
|----------|-----------|--------|
| Локальная машина (прокси) | ❌ | DDoS-Guard режет |
| ВМ 213 (5.172.178.213) | ✅ | Прямой доступ к API |
| Pod в кластере | ✅ | Внутренняя сеть |
**Решение:** все curl через `ssh naeel@5.172.178.213 "curl ..."`.
## Полный поток (5 шагов)
### Шаг 0: Узнать svcOperationId для create
```bash
curl -s --max-time 15 \
-H "Authorization: Bearer $TOKEN" \
-H "User-Agent: $UA" \
"$API/services/1" | jq '.svc.operations[] | select(.operation=="create") | .svcOperationId'
# → 18 (для dummy)
```
### Шаг 0b: Получить список параметров
```bash
curl -s --max-time 15 \
-H "Authorization: Bearer $TOKEN" \
-H "User-Agent: $UA" \
"$API/instanceOperations/default/18" | jq '.svcOperation.cfsParams[] | {svcOperationCfsParamId, svcOperationCfsParam, dataType, isRequired, defaultValue, valueList}'
```
### Шаг 1: Создать инстанс
```bash
RESP=$(curl -s --max-time 15 -i \
-H "Authorization: Bearer $TOKEN" \
-H "User-Agent: $UA" \
-H "Content-Type: application/json" \
-d '{"serviceId":1,"displayName":"my-test-'$(date +%H%M%S)'","descr":""}' \
"$API/instances")
INSTANCE_UID=$(echo "$RESP" | grep -i '^location:' | tr -d '\r' | sed 's|.*/||')
echo "instanceUid=$INSTANCE_UID"
```
**Ответ:** `HTTP/2 201`, тело пустое. UUID в заголовке `Location`.
**Важно:**
- `displayName` должен быть уникальным → добавляем timestamp
- `descr` обязательно (пустая строка ок)
- UUID в Location — **uppercase** (например `6528854D-A4EA-428C-9FA4-68E85FA9B3AC`)
### Шаг 2: Создать операцию
```bash
RESP=$(curl -s --max-time 15 -i \
-H "Authorization: Bearer $TOKEN" \
-H "User-Agent: $UA" \
-H "Content-Type: application/json" \
-d '{"instanceUid":"'$INSTANCE_UID'","operation":"create"}' \
"$API/instanceOperations")
OP_UID=$(echo "$RESP" | grep -i '^location:' | tr -d '\r' | sed 's|.*/||')
echo "opUid=$OP_UID"
```
**Ответ:** `HTTP/2 201`, тело: `{}`. UUID в `Location`.
### Шаг 3: Задать параметры (по одному на каждый)
```bash
# 5 обязательных параметров для dummy create:
# 242 — resourceRealm (string, required, default="dummy")
curl -s --max-time 10 -o /dev/null -w '%{http_code}' \
-H "Authorization: Bearer $TOKEN" -H "User-Agent: $UA" -H "Content-Type: application/json" \
-d '{"instanceOperationUid":"'$OP_UID'","svcOperationCfsParamId":242,"paramValue":"dummy"}' \
"$API/instanceOperationCfsParams"
# → 201
# 198 — durationMs (integer>=0, required, default="0")
curl -s --max-time 10 -o /dev/null -w '%{http_code}' \
-H "Authorization: Bearer $TOKEN" -H "User-Agent: $UA" -H "Content-Type: application/json" \
-d '{"instanceOperationUid":"'$OP_UID'","svcOperationCfsParamId":198,"paramValue":"0"}' \
"$API/instanceOperationCfsParams"
# → 201
# 199 — failAtStart (boolean, required, default="false")
curl -s --max-time 10 -o /dev/null -w '%{http_code}' \
-H "Authorization: Bearer $TOKEN" -H "User-Agent: $UA" -H "Content-Type: application/json" \
-d '{"instanceOperationUid":"'$OP_UID'","svcOperationCfsParamId":199,"paramValue":"false"}' \
"$API/instanceOperationCfsParams"
# → 201
# 200 — failInProgress (boolean, required, default="false")
curl -s --max-time 10 -o /dev/null -w '%{http_code}' \
-H "Authorization: Bearer $TOKEN" -H "User-Agent: $UA" -H "Content-Type: application/json" \
-d '{"instanceOperationUid":"'$OP_UID'","svcOperationCfsParamId":200,"paramValue":"false"}' \
"$API/instanceOperationCfsParams"
# → 201
# 863 — arr (array, required, valueList=["test","2","val1"])
# ⚠️ ВАЖНО: array параметры передаются как JSON-строка!
curl -s --max-time 10 -o /dev/null -w '%{http_code}' \
-H "Authorization: Bearer $TOKEN" -H "User-Agent: $UA" -H "Content-Type: application/json" \
-d '{"instanceOperationUid":"'$OP_UID'","svcOperationCfsParamId":863,"paramValue":"[\"test\"]"}' \
"$API/instanceOperationCfsParams"
# → 201
```
**Важно:**
- Порядок параметров **не важен**
- Все `paramValue`**строки** (даже числа и булевы)
- Array: `"[\"test\"]"`**JSON-строка**, не голый массив
- Map/map-fixed — **JSON-строка** вида `"{\"key\":\"value\"}"`
- Endpoint: `/instanceOperationCfsParams` (НЕ `/instanceOperations/{uid}/params` — такого нет!)
### Шаг 4: Запустить
```bash
curl -s --max-time 15 \
-H "Authorization: Bearer $TOKEN" \
-H "User-Agent: $UA" \
-H "Content-Type: application/json" \
-d '{}' \
"$API/instanceOperations/$OP_UID/run"
# → 201 (тело пустое)
```
**⚠️ БЕЗ /run НЕ РАБОТАЕТ!** HAR браузера показывает auto-start, но через API операция не стартует автоматически. `/run` обязателен.
### Шаг 5: Поллинг статуса
```bash
# Ждать пока operationIsInProgress == false и explainedStatus != "creating"
for i in $(seq 1 60); do
STATUS=$(curl -s --max-time 10 \
-H "Authorization: Bearer $TOKEN" \
-H "User-Agent: $UA" \
"$API/instances?pageSize=200&page=1")
FOUND=$(echo "$STATUS" | jq -r --arg uid "$INSTANCE_UID" \
'.results[] | select(.instanceUid == $uid) | "\(.explainedStatus) inProgress=\(.operationIsInProgress)"')
if [ -n "$FOUND" ]; then
echo "[$i] $FOUND"
if echo "$FOUND" | grep -qv "inProgress=True" && echo "$FOUND" | grep -qv "creating"; then
echo "ГОТОВО!"
break
fi
fi
sleep 5
done
```
## Параметры dummy create (svcOperationId=18)
| ID | Имя | Тип | Обязательный | По умолчанию | valueList |
|----|-----|-----|-------------|-------------|-----------|
| 242 | resourceRealm | string | ✅ | dummy | ["dummy"] |
| 198 | durationMs | integer>=0 | ✅ | 0 | — |
| 199 | failAtStart | boolean | ✅ | false | ["false","true"] |
| 200 | failInProgress | boolean | ✅ | false | ["false","true"] |
| 863 | arr | array | ✅ | — | ["test","2","val1"] |
| 201 | whereFail | integer>0 | ❌ | 1 | ["1","2","3"] |
| 286 | bodymessage | string | ❌ | — | — |
| 321 | mapExample | map | ❌ | — | — |
| 322 | jsonExample | json | ❌ | — | — |
| 396 | nestedRefExample | — | ❌ | — | [] |
| 647 | mapFixed | map-fixed | ❌ | — | — |
| 654 | arrayMapFixedExample | array-map-fixed | ❌ | — | — |
## ⛔ ВСЕ ОШИБКИ (хронология)
### Ошибка 0: DDoS-Guard блокирует curl с локальной машины
- **Симптом:** `403 Forbidden` / пустой ответ
- **Причина:** DDoS-Guard требует `User-Agent: Mozilla/5.0` и всё равно может резать
- **Решение:** curl через ВМ 213 (`ssh naeel@5.172.178.213 "curl ..."`)
### Ошибка 1: `'HttpClient' object has no attribute 'post'` (v1.0.7)
- **Причина:** В http_client.py был только `get()`
- **Исправление:** Добавлен `post()` метод
### Ошибка 2: `r.json()` на пустом ответе (v1.0.8)
- **Причина:** POST /instances → 201 с пустым телом, `r.json()` падает
- **Исправление:** try/except, возвращаем `{}`
### Ошибка 3: instanceUid не извлекался (v1.0.9)
- **Причина:** UUID в заголовке `Location`, не в теле ответа
- **Исправление:** Парсинг Location → UUID
### Ошибка 4: cfsParams в POST /instances (v1.0.10)
- **Симптом:** `400 Bad Request`
- **Причина:** Параметры нельзя передавать при создании инстанса
- **Исправление:** Убраны cfsParams из payload
### Ошибка 5: `descr` обязателен (v1.0.11)
- **Симптом:** `400 Bad Request`
- **Причина:** Поле `descr` required в POST /instances
- **Исправление:** `"descr": ""`
### Ошибка 6: displayName не уникален (v1.0.13)
- **Симптом:** `400 "Instance Display Name not unique"`
- **Причина:** Имя `autotest-1` уже занято
- **Исправление:** Timestamp в имени
### Ошибка 7: cfsParams в POST /instanceOperations (v1.0.23)
- **Симптом:** `422 "required CFS parameter failInProgress (200) is missing"`
- **Причина:** cfsParams нельзя передавать в /instanceOperations — они идут отдельно
- **Исправление:** Параметры через `/instanceOperationCfsParams`
### Ошибка 8: Неправильный endpoint для параметров (v1.0.24)
- **Симптом:** 404 / параметры не применяются
- **Причина:** Использовался `/instanceOperations/{uid}/params` (не существует)
- **Исправление:** Правильный endpoint: `/instanceOperationCfsParams`
### Ошибка 9: Пустые параметры очищали defaults (v1.0.26)
- **Симптом:** defaults не подставлялись
- **Причина:** Пустые paramValue перезаписывали default
- **Исправление:** Отправлять все 12 параметров (HAR analysis показал что так правильно)
### Ошибка 10: Auto-start не работает через API (v1.0.34→1.0.35)
- **Симптом:** Инстанс в статусе `not created` после всех шагов
- **Причина:** HAR показывает auto-start через UI, но через API нужен явный `/run`
- **Исправление:** Добавлен шаг 4 — `POST /instanceOperations/{opUid}/run`
- **Доказательство:** curl без /run → `not created`, curl с /run → `running`
### Ошибка 11: POST body = None → DDoS-Guard блокирует (v1.0.33)
- **Симптом:** Запрос отклонён
- **Причина:** `json=None` → тело пустое → DDoS-Guard режет
- **Исправление:** Всегда `json={}` минимум
### Ошибка 12: Array параметры как raw string (v1.0.31)
- **Симптом:** 400 / неверный формат
- **Причина:** `["test"]` — голый массив, а нужна JSON-строка
- **Исправление:** `"[\"test\"]"` — paramValue как экранированная JSON-строка
## Сводная таблица эндпоинтов CREATE
| Шаг | Метод | Путь | Payload | Ответ |
|-----|-------|------|---------|-------|
| 0 | GET | `/services/{svcId}` | — | `.svc.operations[]` |
| 0b | GET | `/instanceOperations/default/{svcOpId}` | — | `.svcOperation.cfsParams[]` |
| 1 | POST | `/instances` | `{serviceId, displayName, descr}` | 201, Location→instanceUid |
| 2 | POST | `/instanceOperations` | `{instanceUid, operation}` | 201, Location→opUid |
| 3 | POST | `/instanceOperationCfsParams` | `{instanceOperationUid, svcOperationCfsParamId, paramValue}` | 201 (×N) |
| 4 | POST | `/instanceOperations/{opUid}/run` | `{}` | 201 |
| 5 | GET | `/instances?pageSize=200` | — | Поллинг `explainedStatus` |
## Итог
- **Всего итераций:** 19 (v1.0.6 → v1.0.35)
- **Уникальных багов:** 12
- **Ключевой урок:** Всегда сверяться 1:1 с Go-клиентом, не гадать
- **Рабочий proof:** 2026-07-24 14:48, `curl-test-144834`, статус `running`