Возможности API

Документация API для внешней интеграции доступна в интерфейсе Deckhouse Commander в меню настроек в правом верхнем углу.

Для того чтобы использовать это API, нужно выпустить токен. Токен также выпускается в интерфейсе Deckhouse Commander. Токен нужно передавать в заголовке X-Auth-Token.

Что доступно в API

  1. Чтение шаблонов кластеров
    1. GET /api/v1/cluster_templates
    2. GET /api/v1/cluster_templates/:id
  2. Чтение каталогов инвентаря
    1. GET /api/v1/catalogs
    2. GET /api/v1/catalogs/:id
  3. Создание, изменение, удаление кластеров
    1. POST /api/v1/clusters
    2. GET /api/v1/clusters
    3. GET /api/v1/clusters/:id
    4. PUT /api/v1/clusters/:id
    5. DELETE /api/v1/clusters/:id
  4. Создание, изменение, удаление записей в каталогах
    1. POST /api/v1/records
    2. GET /api/v1/records
    3. GET /api/v1/records/:id
    4. PUT /api/v1/records/:id
    5. DELETE /api/v1/records/:id

Создание кластера с использованием записи из каталога

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

Чтобы создать кластер, нам нужен ID версии шаблона. Мы возьмем последнюю версию, которая записана в шаблоне в поле current_cluster_template_version_id. Тело версий шаблона громоздкое, поэтому опустим вывод версий:

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

Нас интересует поле current_cluster_template_version_id:

1{
2  "id": "fb999a72-efe7-4db7-af53-11b17bc0a687",
3  "name": "YC Dev",
4  "current_cluster_template_version_id": "8e75210a-f05c-421d-84b3-fc0697814d6d",
5  "comment": "Канал обновлений и версия k8s задаются",
6  "current_revision": 12,
7  "immutable": false,
8  "created_at": "2024-02-05T17:35:44.318+03:00",
9  "updated_at": "2024-04-10T18:00:57.835+03:00",
10  "archived_at": null,
11  "archive_number": null
12}
1TEMPLATE_VERSION_ID="$(curl -s -X 'GET' \
2        "https://$COMMANDER_HOST/api/v1/cluster_templates/$TEMPLATE_ID?without_archived=true" \
3        -H 'accept: application/json' \
4        -H "X-Auth-Token: $COMMANDER_TOKEN" |
5        jq -r '.current_cluster_template_version_id')"

Получение записи для входных параметров

Получим схему входных параметров и убедимся, что среди них есть запись из каталога yandex-cloud-slot

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

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

1[
2  {
3    "header": "Параметры кластера"
4  },
5  {
6    "key": "slot",
7    "span": 4,
8    "title": "Слот для кластера в Yandex Cloud",
9    "catalog": "yandex-cloud-slot",
10    "immutable": true
11  },
12  {
13    "key": "releaseChannel",
14    "enum": [ "Alpha", "Beta", "EarlyAccess", "Stable", "RockSolid" ],
15    "span": 1,
16    "title": "Канал обновлений",
17    "default": "EarlyAccess"
18  },
19  {
20    "key": "kubeVersion",
21    "enum": [ "Automatic", "1.25", "1.26", "1.27", "1.28", "1.29" ],
22    "span": 1,
23    "title": "Версия Kubernetes",
24    "default": "Automatic"
25  }
26]

Найдем запись из этого каталога. Для начала определим ID каталога по его идентификатору (slug).

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

Теперь выберем первую попавшуюся запись из этого каталога, который еще не занят другим кластером. Эту запись нужно подготовить к использованию в кластере. Для записи необходимо помимо значений указать его ID в специальном поле x-commander-record-id. Это поле названо так, чтобы не вводить ограничение на поле id, которое может потребоваться пользователям в самих записях:

1SLOT_RECORD="$(curl -s -X 'GET' \
2    "https://$COMMANDER_HOST/api/v1/records?without_archived=true" \
3    -H 'accept: application/json' \
4    -H "X-Auth-Token: $COMMANDER_TOKEN" |
5    jq -rc --arg catalog_id "$CATALOG_ID" '[
6            .[] |
7                select(
8                    .catalog_id == $catalog_id
9                    and
10                    .cluster_id == null
11                )
12            ][0]
13            | .values + { "x-commander-record-id": .id }')"

Полученная структура:

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

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

Теперь можем создать кластер:

1PAYLOAD="$(jq -nc --argjson slot "$SLOT_RECORD" '{
2    "name": "Кластер из API",
3    "cluster_template_version_id": "8e75210a-f05c-421d-84b3-fc0697814d6d",
4    "values": {
5        "kubeVersion": "1.29",
6        "releaseChannel": "EarlyAccess",
7        "slot": $slot
8    }
9}')"
10curl -v -X 'POST' \
11    "https://$COMMANDER_HOST/api/v1/clusters" \
12    -H 'accept: application/json' \
13    -H "X-Auth-Token: $COMMANDER_TOKEN" \
14    -H 'Content-Type: application/json' \
15    -d "'$PAYLOAD'"
16
17
18
19curl -v -X 'POST' \
20    "https://$COMMANDER_HOST/api/v1/clusters" \
21    -H 'accept: application/json' \
22    -H "X-Auth-Token: $COMMANDER_TOKEN" \
23    -H 'Content-Type: application/json' \
24    -d '{"name":"Кластер из API","cluster_template_version_id":"8e75210a-f05c-421d-84b3-fc0697814d6d","values":{"kubeVersion":"1.29","releaseChannel":"EarlyAccess","slot":{"ip":"158.160.110.223","name":"b","x-commander-record-id":"5f6727e7-630c-4b18-bcf0-868ea96a27ee"}}}'
25
26
27
28{
29  "name": "Кластер из API",
30  "cluster_template_version_id": "8e75210a-f05c-421d-84b3-fc0697814d6d",
31  "values": {
32      "kubeVersion": "1.29",
33      "releaseChannel": "EarlyAccess",
34      "slot": {
35         "x-commander-record-id": "5f6727e7-630c-4b18-bcf0-868ea96a27ee",
36         "ip": "158.166.177.188",
37         "name": "x"
38       }
39   }
40}
41
42"'" "$(jq -n --argjson slot "$SLOT_RECORD" '{
43        "name": "Кластер из API",
44        "cluster_template_version_id": "8e75210a-f05c-421d-84b3-fc0697814d6d",
45        "values": {
46            "kubeVersion": "1.29",
47            "releaseChannel": "EarlyAccess",
48            "slot": $slot
49        }
50    }')" "'"

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

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

Теперь проследим процесс создания кластера. Мы должны дождаться статуса in_sync:

1cluster_status="$(curl -s -X 'GET' \
2    "https://$COMMANDER_HOST/api/v1/clusters/5436e6ef-d811-472f-9c9c-46cb9c6321d9" \
3    -H 'accept: application/json' \
4    -H "X-Auth-Token: $COMMANDER_TOKEN" |
5    jq -r '.status')"
6
7while [ "in_sync" != "$cluster_status" ]
8do
9    cluster_status="$(curl -s -X 'GET' \
10        "https://$COMMANDER_HOST/api/v1/clusters/5436e6ef-d811-472f-9c9c-46cb9c6321d9" \
11        -H 'accept: application/json' \
12        -H "X-Auth-Token: $COMMANDER_TOKEN" |
13        jq -r '.status')"
14    echo $cluster_status
15    sleep 5
16done
17
18creating
19creating
20creating
21creating
22creating
23creating
24creating
25creating
26creating
27creating
28creating
29creating
30creating
31creating
32creating
33creating
34# ...
35in_sync

Удаление кластера

Если кластер больше не нужен, его можно удалить. В ответ придет состояние кластера, но уже со статусом delete.

1curl -s -X 'DELETE' \
2    "https://$COMMANDER_HOST/api/v1/clusters/5436e6ef-d811-472f-9c9c-46cb9c6321d9" \
3    -H 'accept: application/json' \
4    -H "X-Auth-Token: $COMMANDER_TOKEN"
5
6{
7    "id": "5436e6ef-d811-472f-9c9c-46cb9c6321d9",
8    "current_revision": 1834,
9    "name": "Кластер из API",
10    "values": {
11        "slot": {
12            "ip": "158.166.177.188",
13            "name": "x",
14            "x-commander-record-id": "5f6727e7-630c-4b18-bcf0-868ea96a27ee"
15        },
16        "kubeVersion": "1.29",
17        "createWorker": true,
18        "releaseChannel": "EarlyAccess",
19        "installResources": true
20    },
21    "cluster_template_version_id": "8e75210a-f05c-421d-84b3-fc0697814d6d",
22    "cluster_template_version_switched_at": "2024-04-24T21:40:35.222+03:00",
23    "created_at": "2024-04-24T21:40:35.525+03:00",
24    "updated_at": "2024-04-25T12:57:51.621+03:00",
25    "archived_at": null,
26    "archive_number": null,
27    "was_created": true,
28    "is_locked": true,
29    "cluster_type": "cloud",
30    "status": "delete",
31    "agent_status": null,
32    "agent_api_key": null,
33    "cluster_configuration_applied_at": "2024-04-24T22:10:57.191+03:00",
34    "cluster_configuration_checked_at": "2024-04-25T12:57:45.479+03:00",
35    "resources_sync_state": "no",
36    "resources_state_results": [
37        // ...
38    ],
39    "resources_checked_at": "2024-04-25T12:57:31.084+03:00",
40    "resources_applied_at": "2024-04-24T22:04:59.813+03:00",
41    "init_configuration_rendered": "...",
42    "init_resources_rendered": "...",
43    "dhctl_configuration_rendered": "...",
44    "applied_cluster_configuration_rendered": "...",
45    "applied_provider_specific_cluster_configuration_rendered": "...",
46    "applied_resources_rendered": "......",
47    "desired_cluster_configuration_rendered": "...",
48    "desired_provider_specific_cluster_configuration_rendered": "...",
49    "desired_resources_rendered": "......",
50    "render_errors": [],
51    "cluster_agent_data": [
52        // ...
53    ]
54}