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).
Что произойдёт:
- Mockarty найдёт триггер и убедится что он включён + принадлежит
вашему namespace. - Сгенерирует свежий 64-hex
dispatch_token. - Отрендерит шаблон тела с новым токеном + ID задачи.
- Отправит POST на URL триггера с настроенным auth.
- Распарсит ответ через
statusIdJsonpathи сохранит external job
ID на запуске. - Создаст задачу Mockarty с тем же
dispatch_token. - CI-пайплайн поднимет раннер Mockarty, который вызовет
/api/v1/runner/tasks/pullсclaim_tokens=[<token>]— только он
сможет забрать эту задачу. - 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 и сослаться на него
из триггера:
-
Откройте Settings → Secret stores и создайте store (или
используйте существующий), напримерci-vault. -
Добавьте в этот store ключ, например
gitlab_pat_prod, со
значением реального токена. -
В редакторе триггера в поле Secret укажите:
{{secret:ci-vault/gitlab_pat_prod}}
Mockarty подтянет настоящее значение в момент срабатывания. Токен
никогда не записывается в строку триггера, не возвращается API, не
попадает в логи. Поменяете значение в Secret stores — все
триггеры с такой ссылкой подхватят новый токен при следующем
срабатывании; редактировать их не нужно.
Тот же синтаксис {{secret:store/key}} работает в значениях
заголовков и в шаблоне тела — полезно, когда ваш CI ждёт
специфический заголовок аутентификации, отличный от стандартного
Authorization.
Если ссылка указывает на несуществующий ключ — запуск отказывается
с 422 и именем ссылки (конфигурационная ошибка, исправляется за
секунды). Если сам backend хранилища недоступен — придёт 503
(инфраструктурная проблема — повторите, когда backend поднимется).
Аутентификация из интеграции
Если в Настройки → Интеграции уже настроена интеграция
GitHub, GitLab или Jenkins, триггер может использовать её
как источник аутентификации вместо собственного секрета:
- В редакторе триггера откройте Аутентификация и выберите
Из интеграции. - Выберите интеграцию из списка (подходят только 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.
См. также
- Эфемерные раннеры (CI / Kubernetes) —
механизм runner-claim, на котором построены CI Triggers. - Метки раннеров и таргетинг — как сузить
выбор раннера дополнительными ограничениями.