Стадия жизненного цикла модуля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.

Каждый запрос должен содержать API-ключ в 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

API-ключ принадлежит сервисному аккаунту — идентичности, от имени которой выполняется запрос. Права даёт не ключ, а роли, назначенные аккаунту: все ключи одного аккаунта действуют с одинаковыми правами, а отзыв ключа права не меняет. Ключи выпускаются на вкладке «Сервисные аккаунты» раздела «Пользователи и права» (полная процедура — в разделе API интеграции и сервисные аккаунты руководства пользователя).

Есть два вида сервисных аккаунтов, и вид аккаунта определяет, какие методы API доступны его ключу:

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

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

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

Сервисному аккаунту рабочего пространства контекст не нужен: рабочее пространство определяется самим аккаунтом. Заголовок X-Commander-Workspace-ID принимается только тогда, когда он повторяет это же рабочее пространство.

Глобальный сервисный аккаунт указывает рабочее пространство в заголовке X-Commander-Workspace-ID, в котором передаётся UUID рабочего пространства:

GET /api/v1/clusters HTTP/1.1
Host: $COMMANDER_HOST
X-Auth-Token: $COMMANDER_GLOBAL_TOKEN
X-Commander-Workspace-ID: 7f3b9e6a-2c4d-4f8a-9b1e-5d7c6a8f0b2c
Accept: application/json

Правила одинаковы для всех методов:

Сервисный аккаунт Методы X-Commander-Workspace-ID Результат
Глобальный Глобальные Отсутствует Права из глобальных назначений аккаунта
Глобальный Глобальные Передан 400 invalid_workspace_context
Глобальный Рабочего пространства Корректный ID рабочего пространства Права аккаунта в выбранном рабочем пространстве
Глобальный Рабочего пространства Отсутствует или некорректен 400 invalid_workspace_context
Рабочего пространства Своего рабочего пространства Отсутствует или равен этому РП Права из назначений этого рабочего пространства
Рабочего пространства Другого РП или глобальные Любой 403 Forbidden

Объект другого рабочего пространства, объект несуществующего рабочего пространства и несуществующий объект возвращают 403 Forbidden: API не раскрывает, существует ли недоступный объект.

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

Доступны глобальному сервисному аккаунту с соответствующими глобальными назначениями.

  1. Управление глобальными ролями:
    • GET /api/v1/global_roles;
    • POST /api/v1/global_roles;
    • GET /api/v1/global_roles/:id;
    • PUT /api/v1/global_roles/:id;
    • DELETE /api/v1/global_roles/:id.
  2. Управление глобальными назначениями ролей:
    • GET /api/v1/global_role_bindings;
    • POST /api/v1/global_role_bindings;
    • GET /api/v1/global_role_bindings/:id;
    • PUT /api/v1/global_role_bindings/:id;
    • DELETE /api/v1/global_role_bindings/:id.
  3. Управление рабочими пространствами:
    • GET /api/v1/workspaces;
    • POST /api/v1/workspaces;
    • PUT /api/v1/workspaces/:id;
    • DELETE /api/v1/workspaces/:id.
  4. Управление назначениями в рабочих пространствах:
    • 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.

Доступные методы рабочего пространства

Все методы работают с одним рабочим пространством: для сервисного аккаунта рабочего пространства — с его РП, для глобального — с РП из заголовка X-Commander-Workspace-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;
    • DELETE /api/v1/cluster_templates/:id/permanent.
  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;
    • DELETE /api/v1/clusters/:id/permanent.
  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 (архивируемые).

Исключение — запросы, созданные механизмом согласования создания кластеров: решения по ним принимаются только в веб-интерфейсе. Такой запрос помечен в ответе полем approval_flow: true, и POST /api/v1/cluster_change_requests/:id/approve возвращает для него 409 approval_flow_not_supported. Для подтверждения деструктивных изменений инфраструктуры эндпоинт /approve продолжает работать.

Ошибки

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

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

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

  • 400 invalid_workspace_context — заголовок X-Commander-Workspace-ID отсутствует или некорректен там, где он нужен запросу, либо передан там, где он не допускается. См. Контекст рабочего пространства;
  • 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 и аналогичные) — не повторяйте одобрение: перечитайте запрос на изменение и выберите дальнейшее действие;
  • 409 approval_flow_not_supported на /cluster_change_requests/:id/approve — запрос создан механизмом согласования создания кластеров (approval_flow: true), решение по нему принимается только в веб-интерфейсе; повторные попытки не помогут;
  • 422 invalid — ошибка валидации; детали по полям — в поле data;
  • 429 Too Many Requests — превышен лимит частоты запросов; перед повторной попыткой подождите количество секунд из заголовка ответа Retry-After. Тело ответа не является стабильным контрактом;
  • 500 internal_server_error — неожиданная ошибка бэкенда; безопасно повторять идемпотентные запросы с экспоненциальным backoff.

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

Примеры

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

export COMMANDER_HOST="commander.example.com"
export COMMANDER_GLOBAL_TOKEN="..."  # API-ключ глобального сервисного аккаунта.
export COMMANDER_TOKEN="..."         # API-ключ сервисного аккаунта рабочего пространства.

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

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"
  }
]

Мультиселект с возможностью ввода произвольных значений требует type: "array" и items.suggestions в виде массива (может быть пустым). Значение поля — массив строк; перечисленные suggestions — опции в UI, допустимы и произвольные строки:

{
  "key": "kubeVersions",
  "type": "array",
  "title": "Версии Kubernetes",
  "items": {
    "type": "string",
    "suggestions": ["1.32", "1.33", "1.34"]
  }
}
{
  "key": "tags",
  "type": "array",
  "title": "Теги",
  "items": {
    "type": "string",
    "suggestions": []
  }
}

Строгий мультиселект задаётся через type: "array" и items.enum. Допустимы только перечисленные строки (скалярная строка отклоняется):

{
  "key": "harddrive",
  "type": "array",
  "title": "Выбор диска",
  "items": {
    "enum": ["sdk1", "sdk2"]
  }
}

Найдём каталог по его 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, если инфраструктура уже уничтожена вне Deckhouse Commander либо кластер находится в состоянии, из которого обычный DELETE /api/v1/clusters/:id не может восстановиться. Принудительное удаление убирает запись кластера из Deckhouse 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; принудительное удаление оставьте как инструмент ручного восстановления

Безвозвратное удаление архивного кластера

Используйте DELETE /api/v1/clusters/:id/permanent, чтобы необратимо удалить кластер, который уже находится в архиве. При успехе метод возвращает 204 No Content. Для кластера не из архива возвращается 409 Conflict с error_code: not_archived.

curl -sS -X "DELETE" \
    "https://$COMMANDER_HOST/api/v1/clusters/$CLUSTER_ID/permanent" \
    -H "accept: application/json" \
    -H "X-Auth-Token: $COMMANDER_TOKEN" \
    -w "\n%{http_code}\n"

Безвозвратное удаление убирает запись кластера из Deckhouse Commander и не запускает dhctl destroy. История аудита по кластеру сохраняется; отображение имени опирается на снимок в аудите.

Безвозвратное удаление архивного шаблона кластера

Используйте DELETE /api/v1/cluster_templates/:id/permanent, чтобы необратимо удалить шаблон кластера, который уже находится в архиве и на который не ссылается ни один кластер. При успехе метод возвращает 204 No Content.

curl -sS -X "DELETE" \
    "https://$COMMANDER_HOST/api/v1/cluster_templates/$CLUSTER_TEMPLATE_ID/permanent" \
    -H "accept: application/json" \
    -H "X-Auth-Token: $COMMANDER_TOKEN" \
    -w "\n%{http_code}\n"

Если шаблон не в архиве, API возвращает 409 Conflict с error_code: not_archived. Если на шаблон ещё ссылаются кластеры, API возвращает 409 Conflict с error_code: used_by_clusters.

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

API позволяет получать список проектов, создавать, изменять и удалять проекты типа commander. Проекты типа deckhouse — это проекты DKP (ресурсы Project модуля multitenancy-manager), созданные непосредственно в прикладном кластере и не управляемые Deckhouse Commander; Deckhouse 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"

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

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

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