Стадия жизненного цикла модуля: General Availability
У модуля есть требования для установки

Для Deckhouse Commander доступно технологическое превью активно развивающегося Terraform-провайдера, который работает через API интеграции, описанный в этом разделе. Провайдер в настоящее время поддерживает создание, удаление, изменение кластеров и шаблонов кластеров в одном рабочем пространстве, в котором создан токен доступа к API.

Обзор

Deckhouse Commander предоставляет внешний API для сценариев автоматизации и интеграции — CI/CD, self-service-порталов, IDP-платформ. Интерактивная документация OpenAPI (Swagger) встроена в веб-интерфейс и всегда соответствует версии запущенного бэкенда; чтобы её открыть, нажмите на иконку в виде шестерёнки в верхней панели:

  • на стартовой странице (список рабочих пространств) пункт «Документация API» открывает справочник глобального API по адресу /documentation;
  • на странице рабочего пространства пункт «Документация API» открывает справочник API рабочего пространства по адресу /workspaces/<slug-рабочего-пространства>/auth-tokens/documentation.

Каждый запрос должен содержать токен в HTTP-заголовке X-Auth-Token. Все пути указываются относительно https://$COMMANDER_HOST; в примерах используется переменная окружения $COMMANDER_HOST для адреса сервера и $COMMANDER_GLOBAL_TOKEN / $COMMANDER_TOKEN — для токенов.

GET /api/v1/workspaces HTTP/1.1
Host: $COMMANDER_HOST
X-Auth-Token: $COMMANDER_GLOBAL_TOKEN
Accept: application/json

Существует два типа токенов, каждый выпускается в веб-интерфейсе Commander (полная процедура — в разделе API интеграции руководства пользователя):

  • Глобальный токен — выпускается на начальном экране со списком рабочих пространств, в разделе «Параметры → Токены доступа». Позволяет управлять рабочими пространствами, глобальными ролями и назначениями в рабочих пространствах; не позволяет управлять сущностями внутри рабочих пространств;
  • Токен рабочего пространства — выпускается внутри рабочего пространства, в разделе «Параметры → Токены доступа». Позволяет управлять ресурсами того рабочего пространства, в котором токен был выпущен: кластерами, шаблонами кластеров, каталогами инвентаря, записями каталогов, хранилищами образов контейнеров и проектами. Токен всегда привязан к одному рабочему пространству; попытка обратиться к ресурсам другого рабочего пространства возвращает 403 Forbidden.

Запросы без токена или с недействительным/просроченным токеном возвращают 401 Unauthorized. За исчерпывающими схемами запросов и ответов, перечислениями и HTTP-кодами для каждого эндпоинта всегда обращайтесь к Swagger UI.

Доступно по глобальному токену

  1. Получение списка глобальных ролей:
    • GET /api/v1/global_roles.
  2. Управление рабочими пространствами:
    • GET /api/v1/workspaces;
    • POST /api/v1/workspaces;
    • PUT /api/v1/workspaces/:id;
    • DELETE /api/v1/workspaces/:id.
  3. Управление назначениями в рабочих пространствах:
    • GET /api/v1/workspace_role_bindings;
    • POST /api/v1/workspace_role_bindings;
    • GET /api/v1/workspace_role_bindings/:id;
    • PUT /api/v1/workspace_role_bindings/:id;
    • DELETE /api/v1/workspace_role_bindings/:id.

Доступно по токену рабочего пространства

Все эндпоинты неявно ограничены рабочим пространством, к которому привязан токен. Пути, помеченные (архивируемые), принимают query-параметр archived=true, чтобы включить архивные сущности в ответ; по умолчанию они из ответа исключаются.

  1. Управление шаблонами кластеров:
    • GET /api/v1/cluster_templates (архивируемые);
    • POST /api/v1/cluster_templates;
    • GET /api/v1/cluster_templates/:id;
    • PUT /api/v1/cluster_templates/:id;
    • DELETE /api/v1/cluster_templates/:id.
  2. Управление каталогами инвентаря:
    • GET /api/v1/catalogs (архивируемые);
    • POST /api/v1/catalogs;
    • GET /api/v1/catalogs/:id;
    • PUT /api/v1/catalogs/:id;
    • DELETE /api/v1/catalogs/:id.
  3. Управление записями каталогов:
    • POST /api/v1/records;
    • GET /api/v1/records (архивируемые);
    • GET /api/v1/records/:id;
    • PUT /api/v1/records/:id;
    • DELETE /api/v1/records/:id.
  4. Управление кластерами:
    • POST /api/v1/clusters;
    • GET /api/v1/clusters (архивируемые);
    • GET /api/v1/clusters/:id;
    • GET /api/v1/clusters/:id/status;
    • PUT /api/v1/clusters/:id;
    • DELETE /api/v1/clusters/:id;
    • DELETE /api/v1/clusters/:id/force.
  5. Управление хранилищами образов контейнеров:
    • POST /api/v1/registries;
    • GET /api/v1/registries;
    • GET /api/v1/registries/:id;
    • DELETE /api/v1/registries/:id.
  6. Управление проектами:
    • GET /api/v1/projects;
    • POST /api/v1/projects;
    • GET /api/v1/projects/:id;
    • PUT /api/v1/projects/:id;
    • DELETE /api/v1/projects/:id.
  7. Одобрение запросов на изменение кластера:
    • GET /api/v1/cluster_change_requests;
    • GET /api/v1/cluster_change_requests/:id;
    • POST /api/v1/cluster_change_requests/:id/approve.
  8. Чтение задач кластера и их логов:
    • GET /api/v1/cluster_tasks (архивируемые);
    • GET /api/v1/cluster_task_logs (архивируемые).

Ошибки

Все ошибки возвращаются в едином JSON-конверте:

{
  "error": "Человекочитаемое сообщение",
  "error_code": "invalid",
  "errors": { "name": ["can't be blank"] }
}

Опирайтесь в автоматизации на error_code — это стабильный контракт; сообщение error локализовано и может меняться. Используются стандартные HTTP-коды; полный список для каждого эндпоинта — в Swagger UI. Наиболее частые случаи:

  • 401 no_token / invalid_token — заголовок X-Auth-Token отсутствует, просрочен или не распознан;
  • 403 forbidden — у токена нет прав либо запрос обращается к ресурсу вне его рабочего пространства;
  • 409 stale_object при PUT — ресурс изменён параллельным запросом; перечитайте его, слейте изменения и повторите запрос с новым current_revision;
  • 409 conflict на /cluster_change_requests/:id/approve (already_approved, already_completed, cluster_cannot_be_converged и аналогичные) — не повторяйте одобрение: перечитайте запрос на изменение и выберите дальнейшее действие;
  • 422 invalid — ошибка валидации; детали по полям — в поле data;
  • 500 internal_server_error — неожиданная ошибка бэкенда; безопасно повторять идемпотентные запросы с экспоненциальным backoff.

При опросе кластера учитывайте одновременно status и render_errors: ошибка рендеринга шаблона оставляет кластер в нетерминальном статусе, но заполняет render_errors.

Примеры

Во всех примерах предполагается, что заданы переменные окружения:

export COMMANDER_HOST="commander.example.com"
export COMMANDER_GLOBAL_TOKEN="..."  # глобальный токен
export COMMANDER_TOKEN="..."         # токен рабочего пространства

Создание рабочего пространства

curl -X "POST" \
    "https://$COMMANDER_HOST/api/v1/workspaces" \
    -H "accept: application/json" \
    -H "X-Auth-Token: $COMMANDER_GLOBAL_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"name":"Рабочее пространство из API"}'

В ответ приходят данные рабочего пространства; id понадобится для последующих запросов.

{
    "id": "088b8e03-e7c0-4655-aae7-d11b7609c579",
    "slug": "sftpp",
    "name": "Рабочее пространство из API",
    "comment": null,
    "main": false,
    "current_revision": 0,
    "created_at": "2026-01-15T12:19:57.002+03:00",
    "updated_at": "2026-01-15T12:19:57.002+03:00"
}

Получение глобальной роли

Чтобы назначить глобальную роль пользователю или группе, получите id роли:

curl -s -X "GET" \
    "https://$COMMANDER_HOST/api/v1/global_roles" \
    -H "accept: application/json" \
    -H "X-Auth-Token: $COMMANDER_GLOBAL_TOKEN"

В ответе будут объекты ролей; нужное поле — id:

[
  {
    "id": "818c3814-4973-452e-9985-d75fbe35eb4c",
    "name": "commander-admin",
    "comment": null,
    "labels": {
      "commander.deckhouse.io/owned-by": "commander"
    },
    "current_revision": 0,
    "commander_rules": [
      {
        "id": "455ccf46-f34c-4c75-970d-4dbc361adc68",
        "verbs": ["*"],
        "resources": ["*"],
        "resource_names": null,
        "created_at": "2025-04-30T23:46:18.616+03:00",
        "updated_at": "2025-04-30T23:46:18.616+03:00"
      }
    ],
    "kubernetes_rules": [
      {
        "id": "9713527d-69df-4ecb-9db2-35df194e18b5",
        "verbs": ["*"],
        "resources": ["*"],
        "resource_names": null,
        "api_groups": ["*"],
        "created_at": "2025-06-06T16:56:32.208+03:00",
        "updated_at": "2025-06-06T16:56:32.208+03:00"
      }
    ],
    "created_at": "2025-04-30T23:46:18.567+03:00",
    "updated_at": "2025-04-30T23:46:18.567+03:00"
  }
]

Создание назначения

PAYLOAD="$(jq -nc '{
    "workspace_id": "088b8e03-e7c0-4655-aae7-d11b7609c579",
    "name": "workspace-admins",
    "comment": "Назначение из API",
    "role": {
        "type": "GlobalRole",
        "id": "818c3814-4973-452e-9985-d75fbe35eb4c"
    },
    "subjects": [
        {
            "kind": "User",
            "name": "commander-admin@deckhouse.io"
        }
    ]
}')"

curl -X "POST" \
    "https://$COMMANDER_HOST/api/v1/workspace_role_bindings" \
    -H "accept: application/json" \
    -H "X-Auth-Token: $COMMANDER_GLOBAL_TOKEN" \
    -H "Content-Type: application/json" \
    -d "$PAYLOAD"

Успешный ответ:

{
    "id": "05b3abdf-20a5-447b-823b-8e1a57b46158",
    "name": "workspace-admins",
    "comment": "Назначение из API",
    "current_revision": 0,
    "role_type": "GlobalRole",
    "role": {
        "id": "818c3814-4973-452e-9985-d75fbe35eb4c",
        "name": "commander-admin",
        "comment": null,
        "labels": {
            "commander.deckhouse.io/owned-by": "commander"
        },
        "current_revision": 0,
        "created_at": "2025-04-30T23:46:18.567+03:00",
        "updated_at": "2025-04-30T23:46:18.567+03:00"
    },
    "subjects": [
        {
            "kind": "User",
            "name": "commander-admin@deckhouse.io",
            "created_at": "2026-01-15T14:01:01.858+03:00",
            "updated_at": "2026-01-15T14:01:01.858+03:00"
        }
    ],
    "created_at": "2026-01-15T14:01:01.821+03:00",
    "updated_at": "2026-01-15T14:01:01.821+03:00"
}

Добавление группы пользователей в назначение

PUT /api/v1/workspace_role_bindings/:id целиком заменяет список subjects и требует current_revision для оптимистичной блокировки.

PAYLOAD="$(jq -nc '{
    "current_revision": 0,
    "subjects": [
        {
            "kind": "User",
            "name": "commander-admin@deckhouse.io"
        },
        {
            "kind": "Group",
            "name": "Administrators"
        }
    ]
}')"

curl -X "PUT" \
    "https://$COMMANDER_HOST/api/v1/workspace_role_bindings/05b3abdf-20a5-447b-823b-8e1a57b46158" \
    -H "accept: application/json" \
    -H "X-Auth-Token: $COMMANDER_GLOBAL_TOKEN" \
    -H "Content-Type: application/json" \
    -d "$PAYLOAD"

Получение актуальной версии шаблона

TEMPLATE_ID="fb999a72-efe7-4db7-af53-11b17bc0a687"

curl -s -X "GET" \
    "https://$COMMANDER_HOST/api/v1/cluster_templates/$TEMPLATE_ID?without_archived=true" \
    -H "accept: application/json" \
    -H "X-Auth-Token: $COMMANDER_TOKEN" |
    jq "del(.cluster_template_versions)"

Нужное поле — current_cluster_template_version_id:

{
  "id": "fb999a72-efe7-4db7-af53-11b17bc0a687",
  "name": "YC Dev",
  "current_cluster_template_version_id": "8e75210a-f05c-421d-84b3-fc0697814d6d",
  "comment": "Канал обновлений и версия k8s задаются параметрами",
  "current_revision": 12,
  "immutable": false,
  "created_at": "2024-02-05T17:35:44.318+03:00",
  "updated_at": "2024-04-10T18:00:57.835+03:00",
  "archived_at": null,
  "archive_number": null
}

Сохраним его для последующих шагов:

TEMPLATE_VERSION_ID="$(curl -s -X "GET" \
    "https://$COMMANDER_HOST/api/v1/cluster_templates/$TEMPLATE_ID?without_archived=true" \
    -H "accept: application/json" \
    -H "X-Auth-Token: $COMMANDER_TOKEN" |
    jq -r ".current_cluster_template_version_id")"

Поиск неиспользованной записи в каталоге

Посмотрим схему входных параметров выбранной версии шаблона:

curl -s -X "GET" \
    "https://$COMMANDER_HOST/api/v1/cluster_templates/$TEMPLATE_ID?without_archived=true" \
    -H "accept: application/json" \
    -H "X-Auth-Token: $COMMANDER_TOKEN" |
    jq -r --arg ver "$TEMPLATE_VERSION_ID" '
        .cluster_template_versions[]
        | select(.id == $ver)
        | .params'

Схема предусматривает три обязательных параметра; один из них — запись из каталога yandex-cloud-slot (поле catalog):

[
  { "header": "Параметры кластера" },
  {
    "key": "slot",
    "span": 4,
    "title": "Слот для кластера в Yandex Cloud",
    "catalog": "yandex-cloud-slot",
    "immutable": true
  },
  {
    "key": "releaseChannel",
    "enum": ["Alpha", "Beta", "EarlyAccess", "Stable", "RockSolid"],
    "span": 1,
    "title": "Канал обновлений",
    "default": "EarlyAccess"
  },
  {
    "key": "kubeVersion",
    "enum": ["Automatic", "1.25", "1.26", "1.27", "1.28", "1.29"],
    "span": 1,
    "title": "Версия Kubernetes",
    "default": "Automatic"
  }
]

Найдём каталог по его slug:

CATALOG_ID="$(curl -s -X "GET" \
    "https://$COMMANDER_HOST/api/v1/catalogs?without_archived=true" \
    -H "accept: application/json" \
    -H "X-Auth-Token: $COMMANDER_TOKEN" |
    jq -r '.[] | select(.slug == "yandex-cloud-slot") | .id')"

Возьмём первую запись из этого каталога, не занятую другим кластером (cluster_id == null), и подготовим её к использованию: в значения записи добавим специальное поле x-commander-record-id со ссылкой на ID записи:

SLOT_VALUES="$(curl -s -X "GET" \
    "https://$COMMANDER_HOST/api/v1/records?without_archived=true" \
    -H "accept: application/json" \
    -H "X-Auth-Token: $COMMANDER_TOKEN" |
    jq -c --arg cid "$CATALOG_ID" '
        [ .[]
          | select(.catalog_id == $cid and .cluster_id == null) ][0]
        | .values + { "x-commander-record-id": .id }')"

echo "$SLOT_VALUES"

Результат:

{
  "ip": "158.166.177.188",
  "name": "x",
  "x-commander-record-id": "5f6727e7-630c-4b18-bcf0-868ea96a27ee"
}

Имя ключа x-commander-record-id выбрано так, чтобы не конфликтовать с пользовательскими полями id, которые могут присутствовать в самих записях.

Создание кластера

PAYLOAD="$(jq -nc --argjson slot "$SLOT_VALUES" --arg ver "$TEMPLATE_VERSION_ID" '{
    "name": "Кластер из API",
    "cluster_template_version_id": $ver,
    "values": {
        "kubeVersion": "1.29",
        "releaseChannel": "EarlyAccess",
        "slot": $slot
    }
}')"

curl -X "POST" \
    "https://$COMMANDER_HOST/api/v1/clusters" \
    -H "accept: application/json" \
    -H "X-Auth-Token: $COMMANDER_TOKEN" \
    -H "Content-Type: application/json" \
    -d "$PAYLOAD"

В ответ придут данные кластера. Часть полей (отрендеренная конфигурация, timestamps) в примере ниже опущена для краткости:

{
    "id": "5436e6ef-d811-472f-9c9c-46cb9c6321d9",
    "name": "Кластер из API",
    "values": {
        "slot": {
            "ip": "158.166.177.188",
            "name": "x",
            "x-commander-record-id": "5f6727e7-630c-4b18-bcf0-868ea96a27ee"
        },
        "kubeVersion": "1.29",
        "releaseChannel": "EarlyAccess"
    },
    "cluster_template_version_id": "8e75210a-f05c-421d-84b3-fc0697814d6d",
    "was_created": false,
    "status": "new"
}

Опрос статуса готовности кластера

После создания или обновления кластера нужно опрашивать GET /api/v1/clusters/:id до достижения статуса status: "in_sync". Учитывайте поле render_errors: непустой список означает, что шаблон не отрендерился и операция не продвинется, пока входные значения не будут исправлены через PUT /api/v1/clusters/:id.

CLUSTER_ID="5436e6ef-d811-472f-9c9c-46cb9c6321d9"
DEADLINE=$(( $(date +%s) + 60 * 60 ))  # таймаут 1 час

while : ; do
    RESP="$(curl -sS -X "GET" \
        "https://$COMMANDER_HOST/api/v1/clusters/$CLUSTER_ID" \
        -H "accept: application/json" \
        -H "X-Auth-Token: $COMMANDER_TOKEN")"

    STATUS="$(jq -r ".status" <<<"$RESP")"
    RENDER_ERRORS="$(jq -r ".render_errors // [] | length" <<<"$RESP")"

    echo "status=$STATUS render_errors=$RENDER_ERRORS"

    if [ "$STATUS" = "in_sync" ]; then
        echo "кластер готов"
        break
    fi

    if [ "$RENDER_ERRORS" != "0" ]; then
        echo "ошибка рендеринга шаблона, см. render_errors:"
        jq ".render_errors" <<<"$RESP"
        exit 1
    fi

    if [ "$(date +%s)" -gt "$DEADLINE" ]; then
        echo "таймаут ожидания in_sync"
        exit 1
    fi

    sleep 10
done

Типичная последовательность значений status: newcreatingin_sync. Застревание в creating вместе с непустым render_errors указывает на проблему с шаблоном или входными значениями; после обновления кластера через PUT опрос возобновится.

Получение облегчённого статуса кластера

Для частого опроса и дашбордов используйте GET /api/v1/clusters/:id/status. Эндпоинт возвращает компактный snapshot — текущий статус, активные алерты, сводку Upmeter и данные для подключения к master-узлу — и не содержит чувствительных полей, таких как отрендеренная конфигурация и API-ключ агента.

curl -s -X "GET" \
    "https://$COMMANDER_HOST/api/v1/clusters/$CLUSTER_ID/status" \
    -H "accept: application/json" \
    -H "X-Auth-Token: $COMMANDER_TOKEN" | jq .
{
  "id": "5436e6ef-d811-472f-9c9c-46cb9c6321d9",
  "status": "in_sync",
  "active_alerts": [
    { "name": "KubeletDown", "severity_level": 4 }
  ],
  "upmeter": {
    "status": "up",
    "components": [
      { "name": "control-plane", "status": "up" },
      { "name": "synthetic", "status": "up" }
    ]
  },
  "master_node": {
    "host": "203.0.113.10",
    "user": "ubuntu"
  }
}

active_alerts может быть пустым; upmeter и master_node могут быть null, пока агент кластера ещё не собрал данные.

Удаление кластера и ожидание его полного уничтожения

DELETE /api/v1/clusters/:id только инициирует удаление — в ответе возвращается кластер со статусом status: "delete" и флагом is_locked: true. Полное удаление подтверждается тем, что последующий GET возвращает 404 not_found.

CLUSTER_ID="5436e6ef-d811-472f-9c9c-46cb9c6321d9"

curl -sS -X "DELETE" \
    "https://$COMMANDER_HOST/api/v1/clusters/$CLUSTER_ID" \
    -H "accept: application/json" \
    -H "X-Auth-Token: $COMMANDER_TOKEN" | jq "{id, status, is_locked}"
{
  "id": "5436e6ef-d811-472f-9c9c-46cb9c6321d9",
  "status": "delete",
  "is_locked": true
}

Опрос до полного удаления:

DEADLINE=$(( $(date +%s) + 60 * 60 ))

while : ; do
    HTTP_CODE="$(curl -sS -o /dev/null -w "%{http_code}" -X "GET" \
        "https://$COMMANDER_HOST/api/v1/clusters/$CLUSTER_ID" \
        -H "accept: application/json" \
        -H "X-Auth-Token: $COMMANDER_TOKEN")"

    echo "http_code=$HTTP_CODE"

    if [ "$HTTP_CODE" = "404" ]; then
        echo "кластер полностью удалён"
        break
    fi

    if [ "$(date +%s)" -gt "$DEADLINE" ]; then
        echo "таймаут ожидания удаления"
        exit 1
    fi

    sleep 10
done

Если кластер заблокирован другой операцией (например, идёт converge), DELETE вернёт 409 Conflict:

{
  "error": "Cluster is locked",
  "error_code": "conflict",
  "errors": null
}

Повторите DELETE после завершения блокирующей операции или проверьте ожидающие запросы на изменение кластера через GET /api/v1/cluster_change_requests?cluster_id=$CLUSTER_ID и одобрите/отмените их.

Принудительное удаление кластера

Используйте DELETE /api/v1/clusters/:id/force, если инфраструктура уже уничтожена вне Commander либо кластер находится в состоянии, из которого обычный DELETE /api/v1/clusters/:id не может восстановиться. Принудительное удаление убирает запись кластера из Commander без запуска dhctl destroy и не затрагивает внешние ресурсы — очистите инфраструктуру самостоятельно.

curl -sS -X "DELETE" \
    "https://$COMMANDER_HOST/api/v1/clusters/$CLUSTER_ID/force" \
    -H "accept: application/json" \
    -H "X-Auth-Token: $COMMANDER_TOKEN" | jq "{id, status, is_locked}"

Для штатного удаления используйте DELETE /api/v1/clusters/:id; принудительное удаление оставьте как инструмент ручного восстановления

Управление проектами

API позволяет получать список проектов, создавать, изменять и удалять проекты типа commander. Проекты типа deckhouse — это проекты DKP (ресурсы Project модуля multitenancy-manager), созданные непосредственно в прикладном кластере и не управляемые Deckhouse Commander; Commander их обнаруживает и отображает, но изменение и удаление таких проектов через API запрещено — попытка возвращает 403. Подробнее о типах проектов, статусах и коллизиях имён см. раздел Проекты руководства пользователя.

Все примеры используют токен рабочего пространства (заголовок X-Auth-Token: $COMMANDER_TOKEN).

Получение списка проектов

curl -s -X 'GET' \
    "https://$COMMANDER_HOST/api/v1/projects" \
    -H 'accept: application/json' \
    -H "X-Auth-Token: $COMMANDER_TOKEN"

В ответе возвращается массив проектов рабочего пространства. Ниже приведён сокращённый пример (часть полей опущена для краткости):

[
  {
    "id": "1a0c4f1b-8f24-4d0a-9a21-0c2d7e1a2b3c",
    "name": "web-frontend",
    "kind": "commander",
    "description": "Публичный сайт команды",
    "resource_quota": {
      "requests": {
        "cpu": "2",
        "memory": "4Gi",
        "storage": "50Gi"
      }
    },
    "cumulative_status": "deployed",
    "status_data": {},
    "kubernetes_project_id": "b7c1d1d8-9d0e-4cbb-8d9e-2b77b6b9f9c1",
    "cluster_id": "5436e6ef-d811-472f-9c9c-46cb9c6321d9",
    "cluster_name": "prod-eu-1",
    "administrators": [
      { "kind": "User",  "name": "alice@example.com" },
      { "kind": "Group", "name": "platform-admins" }
    ],
    "error_messages": [],
    "created_at": "2026-04-10T12:00:00Z",
    "synced_at":  "2026-04-23T18:15:32Z"
  }
]

Ключевые поля ответа:

  • id — идентификатор проекта в Deckhouse Commander;
  • name — имя проекта (совпадает с именем namespace в прикладном кластере);
  • kind — тип проекта: commander или deckhouse;
  • cluster_id, cluster_name — прикладной кластер, в котором создан проект;
  • cumulative_status — итоговый статус проекта (см. Статусы проекта);
  • kubernetes_project_id — идентификатор объекта Project в прикладном кластере; заполняется после успешного применения агентом;
  • resource_quota — установленные ресурсные квоты проекта;
  • administrators — пользователи и группы, назначенные администраторами проекта;
  • error_messages — сообщения об ошибках применения проекта, если они есть.

Создание проекта

curl -s -X 'POST' \
    "https://$COMMANDER_HOST/api/v1/projects" \
    -H 'accept: application/json' \
    -H "X-Auth-Token: $COMMANDER_TOKEN" \
    -H 'Content-Type: application/json' \
    -d '{
  "name": "web-frontend",
  "cluster_id": "5436e6ef-d811-472f-9c9c-46cb9c6321d9",
  "description": "Публичный сайт команды",
  "resource_quota": {
    "requests": {
      "cpu": "2",
      "memory": "4Gi",
      "storage": "50Gi"
    }
  },
  "administrators": [
    { "kind": "User",  "name": "alice@example.com" },
    { "kind": "Group", "name": "platform-admins" }
  ]
}'

Параметры запроса:

  • name (обязательный) — имя проекта. Должно соответствовать правилам именования namespace в Kubernetes, зарезервированные системные имена (в частности, default и deckhouse) использовать нельзя, уникальность проверяется в пределах кластера.
  • cluster_id (обязательный) — идентификатор прикладного кластера. После создания проекта менять нельзя.
  • administrators (обязательный, минимум один элемент) — массив объектов {"kind": "User"|"Group", "name": "..."}. Значение kind принимает только User или Group.
  • description — произвольное описание проекта.
  • resource_quota — ресурсные квоты; поддерживаются ключи requests.cpu, requests.memory, requests.storage. Ограничения (limits) в текущей итерации не задаются.

В успешном ответе 201 Created возвращается тело объекта проекта, идентичное формату в получении списка. Сразу после создания cumulative_status обычно равен initialized и со временем переходит в synchronization, а затем в deployed.

При нарушении правил валидации возвращается 422 Unprocessable Entity, например при попытке сохранить проект без администраторов:

{
  "errors": {
    "administrators": ["В проекте должен быть назначен хотя бы один администратор"]
  }
}

Изменение проекта

curl -s -X 'PUT' \
    "https://$COMMANDER_HOST/api/v1/projects/1a0c4f1b-8f24-4d0a-9a21-0c2d7e1a2b3c" \
    -H 'accept: application/json' \
    -H "X-Auth-Token: $COMMANDER_TOKEN" \
    -H 'Content-Type: application/json' \
    -d '{
  "name": "web-frontend",
  "description": "Публичный сайт команды (прод)",
  "resource_quota": {
    "requests": {
      "cpu":     "4",
      "memory":  "8Gi",
      "storage": "100Gi"
    }
  },
  "administrators": [
    { "kind": "User",  "name": "alice@example.com" },
    { "kind": "User",  "name": "bob@example.com" },
    { "kind": "Group", "name": "platform-admins" }
  ]
}'

Тело запроса такое же, как при создании, за исключением поля cluster_id — изменить прикладной кластер у существующего проекта нельзя. В ответе возвращается обновлённый объект проекта.

Попытка изменить проект типа deckhouse возвращает 403 Forbidden.

Удаление проекта

Удаление проекта через API — необратимая операция. Вместе с проектом в прикладном кластере удаляются все созданные в нём ресурсы (namespace, рабочие нагрузки, конфигурации и секреты). Отменить удаление нельзя, и ни сам проект, ни его ресурсы восстановлению не подлежат. Перед выполнением DELETE-запроса убедитесь, что проект и все его данные больше не нужны — в интеграционном сценарии рекомендуется явно подтверждать намерение удалить проект на стороне вызывающей системы.

curl -s -X 'DELETE' \
    "https://$COMMANDER_HOST/api/v1/projects/1a0c4f1b-8f24-4d0a-9a21-0c2d7e1a2b3c" \
    -H 'accept: application/json' \
    -H "X-Auth-Token: $COMMANDER_TOKEN"

Commander снимает с проекта служебные лейблы и запрашивает у агента удаление ресурса Project в прикладном кластере. В ответе возвращается тело удаляемого проекта с обновлённым статусом.

Удаление проектов типа deckhouse через API недоступно и возвращает 403 Forbidden; такие проекты удаляются непосредственно в Deckhouse с помощью модуля multitenancy-manager.

Управление участниками проекта (ресурсы ProjectRoleBinding) и пользовательскими шаблонами проектов через API интеграции в текущем релизе не поддерживаются и появятся в следующих релизах.