Files
autotest/DOCS/api-create-flow.md
2026-07-27 22:05:19 +04:00

12 KiB
Raw Permalink Blame History

⚠️ LEGACY — НЕАКТУАЛЬНО. Исторический документ.

Полный поток CREATE — рабочая схема (доказано curl 2026-07-24)

Базовые константы

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

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: Получить список параметров

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: Создать инстанс

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: Создать операцию

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: Задать параметры (по одному на каждый)

# 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: Запустить

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: Поллинг статуса

# Ждать пока 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