# Полный поток 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`