Документация CI Триггеры — запуск пайплайнов через вебхуки

CI Триггеры — запуск пайплайнов через вебхуки

CI Триггеры позволяют запускать нагрузочный тест, fuzz-кампанию, Test
Plan или любую другую задачу Mockarty на эфемерном раннере,
который поднимается вашим CI/CD пайплайном. Mockarty отправляет
вебхук (GitLab, GitHub Actions, Jenkins, CircleCI или произвольный
HTTP-эндпоинт), передаёт одноразовый dispatch-token в payload и
ждёт, пока запущенный раннер сам заберёт задачу и отрапортует
результат.

Эта страница описывает функционал CI Triggers из Фазы 4 плана
Эфемерных раннеров.

CI Триггеры входят в базовую поставку — отдельная лицензия не
требуется.


Когда использовать

CI Триггеры полезны когда вы хотите держать запуск тестов внутри
вашей CI-инфраструктуры (приватные GitLab Runners, GitHub-hosted
runners с секретами, on-prem Jenkins-агенты), а не поднимать
долгоживущий Mockarty-раннер рядом с админ-узлом.

Типичные сценарии:

  • Нагрузочный тест на каждый PR — каждый pull request открывает
    child-pipeline GitLab, который запускает короткий perf против
    preview-окружения.
  • Ночной fuzzing — расписанный workflow GitHub Actions просит
    Mockarty профаззить API, получает Docker-образ раннера, и удаляет
    раннер по окончании кампании.
  • Smoke из chatops — Slack-команда /perf пинает Mockarty,
    который запускает Jenkins-job, поднимающий раннер.

Концепции

Термин Значение
Trigger (Триггер) Сохранённая конфигурация: URL + метод + шаблон тела + auth + правила опроса статуса. Создаётся один раз, переиспользуется на каждом запуске.
Run (Запуск) Один запуск через триггер. Связан с задачей Mockarty через dispatch-token.
Dispatch token 64-символьный одноразовый секрет, который Mockarty генерирует на каждый запуск и вшивает в тело триггера. Раннер предъявляет этот токен при claim-е задачи — благодаря этому задачу заберёт только он, а не любой другой случайный раннер.
External job ID ID, который вернул CI-эндпоинт (pipeline.id, GitHub run_id, номер сборки Jenkins, и т.д.). Извлекается через JSONPath из тела ответа.
Status polling Mockarty в фоновом режиме периодически опрашивает дополнительный URL, чтобы отслеживать состояние внешнего job. Когда состояние переходит в success/failure — связанная задача Mockarty закрывается с тем же исходом.

Создание триггера

Триггеры управляются через REST API — отдельной UI-страницы для них нет,
поэтому используйте API напрямую или небольшую shell-обёртку.

Встроенные шаблоны (GET /api/v1/ci/templates) предзаполняют
дефолты для распространённых провайдеров:

  • GitLab Pipeline — create-pipeline API (POST /projects/:id/pipeline) с массивом variables; один PAT в PRIVATE-TOKEN покрывает и запуск, и опрос статуса.
  • GitHub workflow_dispatch — событие repository_dispatch.
  • Jenkins Build — параметризованный job.
  • CircleCI Pipeline — v2 endpoint.
  • Custom Webhook — чистый лист.

Чтобы протестировать сохранённый триггер — отправьте POST /api/v1/ci/triggers/{id}/test: Mockarty сделает dry-run против вашего
CI-эндпоинта и вернёт отрендеренный URL запроса, тело, статус ответа и
распарсенный external job ID без создания задачи Mockarty. Несохранённую
конфигурацию можно проверить так же через POST /api/v1/ci/trigger-test
(конфиг триггера — в теле запроса; передайте id, чтобы наложить поля
на сохранённый триггер — пустой authSecret оставит сохранённый
секрет). Кнопка Тестовый запуск в редакторе работает именно так,
поэтому URL, тело и авторизацию можно подбирать ещё до первого
сохранения.

Для сохранённого триггера тело /test необязательно. Передайте
{"extra":{"BRANCH":"main"}}, чтобы проверить подстановку тестовых
переменных окружения CI. Некорректный JSON возвращает 400 без тестовой отправки.

Таблица триггеров в Настройки → CI Triggers показывает последний
запуск каждого триггера (статус + время), а кнопка с часами открывает
полную историю запусков — статус, связанная задача, внешний job ID,
тайминги и последняя ошибка по каждой отправке. По API недавние запуски
доступны через GET /api/v1/ci/runs?triggerId={id}&limit=50.

curl -X POST "$MOCKARTY/api/v1/ci/triggers?namespace=team-a" \
  -H "X-API-Key: $MOCKARTY_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "gitlab-perf",
    "templateKind": "gitlab_pipeline",
    "triggerUrl": "https://gitlab.example.com/api/v4/projects/42/pipeline",
    "triggerBodyTemplate": "{\n  \"ref\": \"main\",\n  \"variables\": [\n    {\"key\": \"MOCKARTY_DISPATCH_TOKEN\", \"value\": \"{{.DispatchToken}}\"},\n    {\"key\": \"MOCKARTY_TASK_ID\", \"value\": \"{{.TaskID}}\"}\n  ]\n}",
    "statusUrlTemplate": "https://gitlab.example.com/api/v4/projects/42/pipelines/{{.ExternalJobID}}",
    "statusIdJsonpath": "$.id",
    "statusStateJsonpath": "$.status",
    "statusSuccessValues": ["success"],
    "statusFailureValues": ["failed", "canceled"],
    "statusPendingValues": ["created", "pending", "running", "preparing", "scheduled"],
    "pollIntervalSec": 10,
    "pollTimeoutSec": 1800,
    "authKind": "header",
    "authSecret": "PRIVATE-TOKEN: glpat-xxxxxxxxxxxxxxxx",
    "enabled": true
  }'

В ответе будет сгенерированный id. Поле authSecret на всех
последующих GET’ах возвращается как ***.


Запуск задачи через триггер

Нагрузочный тест

cURL

curl -X POST "$MOCKARTY/api/v1/perf/run" \
  -H "X-API-Key: $MOCKARTY_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "configId": "perf-config-uuid",
    "ciTriggerId": "trigger-uuid"
  }'

Go SDK

task, err := client.Perf().RunWithOptions(ctx, mockarty.PerfRunRequest{
    ConfigID:    "perf-config-uuid",
    CITriggerID: "trigger-uuid",
})
// task.ID — идентификатор запущенной задачи; используйте его для опроса статуса.

Python SDK

task = client.perf.run({
    "configId": "perf-config-uuid",
    "ciTriggerId": "trigger-uuid",
})
# опрос связанного CI-прогона:
ci_run = client.ci_triggers.get_run_by_task(task.task_id)

Java SDK

Map<String, Object> req = new HashMap<>();
req.put("configId", "perf-config-uuid");
req.put("ciTriggerId", "trigger-uuid");
Map<String, Object> task = client.perf().run(req);

CLI: флага --ci-trigger на mockarty-cli perf run нет намеренно — для запуска CI-цепочки из pipeline POSTите JSON напрямую (триггер можно найти по имени через mockarty-cli ci triggers list).

Что произойдёт:

  1. Mockarty найдёт триггер и убедится что он включён + принадлежит
    вашему namespace.
  2. Сгенерирует свежий 64-hex dispatch_token.
  3. Отрендерит шаблон тела с новым токеном + ID задачи.
  4. Отправит POST на URL триггера с настроенным auth.
  5. Распарсит ответ через statusIdJsonpath и сохранит external job
    ID на запуске.
  6. Создаст задачу Mockarty с тем же dispatch_token.
  7. CI-пайплайн поднимет раннер Mockarty, который вызовет
    /api/v1/runner/tasks/pull с claim_tokens=[<token>] — только он
    сможет забрать эту задачу.
  8. Mockarty опрашивает statusUrlTemplate по расписанию триггера;
    когда внешнее состояние переходит в success/failure —
    локальная задача закрывается с тем же исходом.

Fuzz-запуск

Точно так же — добавьте ciTriggerId в тело POST /api/v1/fuzzing/run.

Если dispatch не удался (CI-эндпоинт лежит, неверный auth, ошибка
рендера шаблона) — запуск отказывается с HTTP 422 и понятным
сообщением; задача Mockarty не создаётся вообще.

Запуск Test Plan

POST /api/v1/test-plans/:id/run принимает то же поле ciTriggerId
(плюс необязательный объект ciEnv — см. раздел про передачу
переменных окружения ниже).
Триггер срабатывает один раз на весь план, а один CI-раннер,
поднятый вашим пайплайном, берёт и выполняет каждый пункт плана
от начала до конца.

curl -X POST $MOCKARTY/api/v1/test-plans/$PLAN_ID/run \
  -H "X-API-Key: $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ciTriggerId":"'"$TRIGGER_ID"'"}'

Запуск Test Case

Test case тоже можно запускать через CI-триггер. Выберите
сохранённый триггер в выпадающем списке Запустить через
CI-триггер
в диалоге запуска на странице тест-кейса, либо
передайте ciTriggerId в API-запросе:

curl -X POST $MOCKARTY/api/v1/namespaces/$NS/test-cases/$CASE_ID/run \
  -H "X-API-Key: $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ciTriggerId":"'"$TRIGGER_ID"'"}'

Mockarty сначала дёрнет CI-триггер. Если всё прошло успешно —
тест-кейс стартует, а ваша внешняя CI-система отслеживает прогресс:

curl "$MOCKARTY/api/v1/ci/runs?taskId=$CASE_RUN_ID" \
  -H "X-API-Key: $TOKEN"

Если что-то пошло не так — ничего не стартует, вы получаете
понятную ошибку и можете повторить запуск, когда причина устранена:

  • Триггер не существует, выключен или принадлежит другому
    workspace → 422.
  • Внешний CI недоступен (сетевая ошибка, таймаут, плохой ответ)
    → 502.
  • ciTriggerId — невалидный UUID → 400.

Если оставить ciTriggerId пустым (или не передавать его вовсе) —
тест-кейс запустится обычным способом, без внешнего CI.


Доступные переменные

В шаблонах тела и URL’а статуса доступны эти поля через синтаксис
Go text/template. Отсутствующие top-level поля приводят к ошибке;
отсутствующие ключи Extra рендерятся пустой строкой.

Переменная Доступна Описание
{{.TaskID}} везде ID задачи Mockarty (UUID).
{{.DispatchToken}} везде Одноразовый 64-hex токен.
{{.Namespace}} везде Владеющий namespace.
{{.CoordinatorURL}} везде URL, на который раннер должен подключиться обратно.
{{.User}} везде Email запустившего пользователя.
{{.Labels}} везде []string требуемых меток раннера.
{{.LabelExpr}} везде Фаза 3 DSL-выражение по меткам (опционально).
{{.TaskType}} везде "performance" / "fuzzing" / "api_test" / ...
{{.ExternalJobID}} только status URL ID, возвращённый CI-эндпоинтом. На момент запуска — пустой.
{{.Now}} везде Текущее UTC-время.
{{.Extra.foo}} везде Одна переменная окружения запуска (см. ниже).

Функции в шаблонах: json, jsonEscape, join, upper, lower,
replace, default, now, gitlabVars, githubInputs, jenkinsParams.


Передача переменных окружения в CI-джобу

При запуске прогона через триггер можно прикрепить переменные окружения,
которые будут переданы в CI-джобу — передаются на каждый запуск, без
зашивания в сам триггер.

  • В UI: выберите CI-триггер в диалоге запуска, затем добавьте строки
    КЛЮЧ / значение в появившейся сетке Переменные окружения.
  • В API: добавьте объект ciEnv в запрос запуска:
curl -X POST "$MOCKARTY/api/v1/namespaces/default/test-cases/$CASE_ID/run" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"ciTriggerId": "...", "ciEnv": {"BRANCH": "main", "SUITE": "smoke"}}'

Ключи должны быть обычным env-идентификатором ([A-Za-z_][A-Za-z0-9_]*); до
50 переменных, значение до 8 КиБ. Некорректные ключи отклоняются с понятной
ошибкой.

Встроенные шаблоны автоматически прокидывают каждую переданную переменную в
правильном для провайдера синтаксисе через эти хелперы:

Хелпер Рендерит (на переменную) Где
{{gitlabVars .Extra}} , {"key": "KEY", "value": "value"} массив variables GitLab
{{githubInputs .Extra}} , "KEY": "value" GitHub client_payload / CircleCI parameters
{{jenkinsParams .Extra}} &KEY=value (URL-кодировка) тело Jenkins build-with-parameters

Поставьте хелпер сразу после последней фиксированной записи — он сам выводит
ведущий разделитель, поэтому пустая мапа ничего не добавляет. Запуск с
ciEnv: {BRANCH: "main"} на GitLab-шаблоне отрендерит
{"key": "BRANCH", "value": "main"} в массиве variables.

Превью до сохранения: кнопка Test у триггера (POST /api/v1/ci/triggers/:id/test) принимает тот же объект extra и показывает
отрендеренное тело.


Режимы auth

Режим Формат authSecret Что отправляется
none (пусто) Без auth-заголовка.
bearer glpat-xxxx Authorization: Bearer glpat-xxxx
basic user:pass или b64:<encoded> Authorization: Basic <base64>
header Header-Name: value Этот заголовок целиком.

authSecret redacted на каждом API-ответе (возвращается как
***). Чтобы обновить триггер без перенабора секрета, отправьте
PATCH с пустым authSecret — текущее значение сохранится.


Маппинг статусов

Mockarty классифицирует внешнее состояние на каждом опросе:

  • Состояние совпадает с одним из statusSuccessValues → задача
    успешна.
  • Совпадает с statusFailureValues → задача fails, в качестве
    сообщения об ошибке берётся значение из statusErrorJsonpath.
  • Совпадает с statusPendingValues → продолжаем опрашивать.
  • Иначе — продолжаем опрашивать (логируется на debug-уровне).

Опрос прекращается через pollTimeoutSec секунд с сообщением
CI: status poll timeout after Ns. Last state: <state>.


Встроенные шаблоны

GET /api/v1/ci/templates возвращает каталог, который использует UI
для picker’а. Каждая запись предзаполняет URL-плейсхолдер, шаблон
тела, JSONPath-дефолты и success/failure/pending-наборы значений для
названного провайдера.


Безопасное хранение секретов

Можно вставить долгоживущий API-токен прямо в редактор триггера, но
тогда при ротации токена придётся править каждый триггер. Лучше
сохранить токен один раз в Mockarty Secret Store и сослаться на него
из триггера:

  1. Откройте Settings → Secret stores и создайте store (или
    используйте существующий), например ci-vault.

  2. Добавьте в этот store ключ, например gitlab_pat_prod, со
    значением реального токена.

  3. В редакторе триггера в поле Secret укажите:

    {{secret:ci-vault/gitlab_pat_prod}}
    

Mockarty подтянет настоящее значение в момент срабатывания. Токен
никогда не записывается в строку триггера, не возвращается API, не
попадает в логи. Поменяете значение в Secret stores — все
триггеры с такой ссылкой подхватят новый токен при следующем
срабатывании; редактировать их не нужно.

Тот же синтаксис {{secret:store/key}} работает в значениях
заголовков и в шаблоне тела — полезно, когда ваш CI ждёт
специфический заголовок аутентификации, отличный от стандартного
Authorization.

Если ссылка указывает на несуществующий ключ — запуск отказывается
с 422 и именем ссылки (конфигурационная ошибка, исправляется за
секунды). Если сам backend хранилища недоступен — придёт 503
(инфраструктурная проблема — повторите, когда backend поднимется).


Аутентификация из интеграции

Если в Настройки → Интеграции уже настроена интеграция
GitHub, GitLab или Jenkins, триггер может использовать её
как источник аутентификации вместо собственного секрета:

  1. В редакторе триггера откройте Аутентификация и выберите
    Из интеграции.
  2. Выберите интеграцию из списка (подходят только GitHub / GitLab /
    Jenkins — это CI-системы, которые Mockarty умеет вызывать).

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

Интеграция Что отправляет триггер
GitHub Authorization: Bearer <токен>
GitLab PRIVATE-TOKEN: <токен>
Jenkins Authorization: Basic <user>:<api-токен>

Ротируйте токен один раз в Настройки → Интеграции — все связанные
триггеры подхватят его автоматически, включая опросы уже идущих
запусков. Через API связь задаётся полем integrationId на триггере;
пустая строка отвязывает интеграцию и возвращает поведение
authKind/authSecret. Сохранение триггера с выключенной
интеграцией, без креда или неподдерживаемого вида вернёт 422 с
точной причиной.

Провайдер GitHub Actions

Когда вы привязываете Test Plan к интеграции GitHub, Mockarty
запускает реальный GitHub Actions run, ведёт его вживую до финального
статуса и умеет его останавливать — не выходя из Mockarty.

Адресация. Заполните поля конфигурации так:

Поле Значение Пример
Project reference owner/repo (можно owner/repo:workflow.yml, чтобы указать workflow прямо тут) octo-org/checkout
Workflow Имя файла workflow или его числовой id ci.yml
Ветка Git-ref, на котором запускается workflow main

Чтобы запустить событие repository_dispatch вместо
workflow_dispatch, задайте в поле workflow значение
repository_dispatch:<event-type> (например
repository_dispatch:mockarty-trigger). В workflow должен быть объявлен
соответствующий триггер:

on:
  workflow_dispatch:      # для режима по умолчанию
    inputs:
      deploy_env:
        type: string
  repository_dispatch:    # для repository_dispatch:<event-type>
    types: [mockarty-trigger]

Переменные. defaultVariables конфигурации (и любые override при
запуске) уходят как inputs для workflow_dispatch или как
client_payload для repository_dispatch.

Поиск run’а. GitHub принимает dispatch без возврата id запуска,
поэтому Mockarty сам находит только что созданный run и начинает его
отслеживать — триггер срабатывает, и через несколько секунд вы видите
живой GitHub-run и его URL в Mockarty.

Права токена. Personal-access token на интеграции должен иметь
repo (читать run) и workflow (запускать его). Для
fine-grained токена нужен доступ Actions: read and write на нужных
репозиториях.

GitHub Enterprise Server. Укажите в base_url интеграции адрес
вашего аплайанса (например https://github.mycorp.example) — Mockarty
сам обратится к его REST API.


Готовые шаблоны CI pipeline’ов

Mockarty дёргает ваш триггер, а CI-сторона поднимает Mockarty-раннер,
который забирает реальную работу по dispatch-токену. Шаблоны ниже
покрывают сторону раннера для четырёх самых распространённых CI.
Положите файл рядом с существующим pipeline-файлом, задайте нужные
секреты (обычно — Mockarty integration token + URL админа), и
триггер заработает end-to-end.

GitLab CI (.gitlab-ci.yml)

mockarty-runner:
  stage: test
  image: ghcr.io/mockarty/mockarty-runner:latest
  variables:
    # Mockarty прокидывает эти значения в тело запроса при
    # срабатывании триггера; пробросьте их через переменные
    # пайплайна — раннер заберёт ту же задачу.
    MOCKARTY_URL: $MOCKARTY_URL
    MOCKARTY_INTEGRATION_TOKEN: $MOCKARTY_INTEGRATION_TOKEN
    MOCKARTY_DISPATCH_TOKEN: $DISPATCH_TOKEN
  rules:
    - if: '$DISPATCH_TOKEN'
  script:
    - mockarty-runner serve
        --coordinator="$MOCKARTY_URL"
        --token="$MOCKARTY_INTEGRATION_TOKEN"
        --claim-token="$MOCKARTY_DISPATCH_TOKEN"
        --ephemeral

URL триггера: https://gitlab.example.com/api/v4/projects/<id>/pipeline
Шаблон тела:

{
  "token": "{{secret:ci-vault/gitlab_pipeline_token}}",
  "ref": "main",
  "variables": [{"key": "DISPATCH_TOKEN", "value": "{{.DispatchToken}}"}]
}

GitHub Actions (.github/workflows/mockarty.yml)

name: Mockarty runner
on:
  repository_dispatch:
    types: [mockarty-run]
jobs:
  serve:
    runs-on: ubuntu-latest
    steps:
      - uses: docker://ghcr.io/mockarty/mockarty-runner:latest
        with:
          args: serve
            --coordinator=${{ secrets.MOCKARTY_URL }}
            --token=${{ secrets.MOCKARTY_INTEGRATION_TOKEN }}
            --claim-token=${{ github.event.client_payload.dispatch_token }}
            --ephemeral

URL триггера: https://api.github.com/repos/<org>/<repo>/dispatches
Auth: Bearer, секрет {{secret:ci-vault/github_pat}} (PAT со
scope repo или fine-grained токен с Contents: read +
Actions: write).
Шаблон тела:

{
  "event_type": "mockarty-run",
  "client_payload": { "dispatch_token": "{{.DispatchToken}}" }
}

Jenkins (Jenkinsfile)

pipeline {
  agent { docker { image 'ghcr.io/mockarty/mockarty-runner:latest' } }
  parameters {
    string(name: 'DISPATCH_TOKEN', defaultValue: '')
  }
  stages {
    stage('Serve') {
      when { expression { return params.DISPATCH_TOKEN?.trim() } }
      steps {
        sh """
          mockarty-runner serve \\
            --coordinator="\$MOCKARTY_URL" \\
            --token="\$MOCKARTY_INTEGRATION_TOKEN" \\
            --claim-token="\$DISPATCH_TOKEN" \\
            --ephemeral
        """
      }
    }
  }
}

URL триггера:
https://jenkins.example.com/job/<job>/buildWithParameters?token=<job_token>
Auth: Basic, секрет {{secret:ci-vault/jenkins_basic}} (пара
user:password в base64).
Шаблон тела:

DISPATCH_TOKEN={{.DispatchToken}}

CircleCI (config.yml + pipeline trigger)

version: 2.1
parameters:
  dispatch_token:
    type: string
    default: ""
jobs:
  serve:
    docker:
      - image: ghcr.io/mockarty/mockarty-runner:latest
    steps:
      - run: |
          mockarty-runner serve \
            --coordinator="$MOCKARTY_URL" \
            --token="$MOCKARTY_INTEGRATION_TOKEN" \
            --claim-token="<< pipeline.parameters.dispatch_token >>" \
            --ephemeral
workflows:
  mockarty:
    when: << pipeline.parameters.dispatch_token >>
    jobs:
      - serve

URL триггера: https://circleci.com/api/v2/project/<slug>/pipeline
Auth: Header, секрет {{secret:ci-vault/circle_token}} мапится
на Circle-Token: ….
Шаблон тела:

{
  "branch": "main",
  "parameters": { "dispatch_token": "{{.DispatchToken}}" }
}

Модель безопасности

  • Namespace scoping — триггеры и запуски привязаны к namespace.
    Вызывающий из NS-A не может прочитать или отменить
    триггер/запуск в NS-B; API возвращает 404 (существование не
    раскрывается), а не 403.
  • Одноразовые токены — один dispatch-токен позволяет
    ровно одному раннеру забрать ровно одну задачу. Утечка токена
    ни на что больше не годится.
  • Redacted secrets — ваш auth-секрет никогда не возвращается
    API в открытом виде.
  • Удаление — в Корзину — удалённый триггер сразу исчезает из всех
    списков и попадает в Корзину пространства имён (тип CI-триггер),
    откуда его можно восстановить до конца срока хранения. Уже начатые им
    запуски сохраняют историю и возвращаются вместе с ним; окончательная
    очистка удаляет запуски вместе с триггером и отказывает, пока один из
    них ещё выполняется. API отвечает на удаление полем
    recoverable: true.

Ограничения

  • В момент кратковременной передачи leadership в кластере опрос
    статуса делает паузу на один тик; активные CI-запуски не теряются.
  • Встроенного «отменить CI-job» callback’а нет — POST /ci/runs/:id/cancel помечает локальный запуск как отменённый и
    завершает связанную задачу, но НЕ дёргает remote-cancel API.

См. также