Skip to content

New API - #806

Open
TomasKorbar wants to merge 3 commits into
packit:mainfrom
TomasKorbar:consolidation_triggering
Open

New API#806
TomasKorbar wants to merge 3 commits into
packit:mainfrom
TomasKorbar:consolidation_triggering

Conversation

@TomasKorbar

Copy link
Copy Markdown
Collaborator
  • Add HTTP API server for submitting consolidation jobs
  • Add Jira webhook command endpoint for bot-mention dispatching

@TomasKorbar
TomasKorbar force-pushed the consolidation_triggering branch 4 times, most recently from 347b96b to e0072e2 Compare September 8, 2026 13:18

@nforro nforro left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems there are some missing pieces, here are some LLM findings:

  1. P1: Public unauthenticated job submission

    ymir/api/consolidation.py:80-97,146-148 exposes /api/consolidation without authentication through openshift/route-api.yml. Anyone reaching the route can enqueue costly arbitrary workflows. Additionally, webhook authentication fails open when JIRA_WEBHOOK_SECRET is unset (ymir/api/jira_webhook.py:65-69). Authentication should cover every mutating endpoint and fail closed.

  2. P1: API image is never published

    openshift/imagestream-api.yml:10 imports quay.io/jotnar/ymir-api:latest, but .github/workflows/build-and-push.yml has no job publishing Containerfile.api. After merge, deployment will receive no new API image.

  3. P1: Deployment requires an undocumented secret

    openshift/deployment-api.yml:50-51 references mandatory secret api-env, but openshift/README.md:11-46 neither documents nor creates it. Unless provisioned out-of-band, the pod remains in CreateContainerConfigError.

  4. P2: None of the 30 API tests run in CI

    Makefile.tests:29-52 enumerates existing package test directories but omits ymir/api/tests. Current green checks therefore provide no test coverage for the new server.

  5. P2: source_issues accepts invalid cardinalities

    ymir/api/consolidation.py:31,109 accepts zero, one, or more than two issues, although consolidation operates on a pair. One issue queues a successful no-op; more than two populates all branches while ymir/agents/prompts/mr_consolidation/prompt.j2:7-13 describes only the first two. Constrain this to exactly two entries when supplied.

TomasKorbar and others added 2 commits September 9, 2026 09:58
Introduce a lightweight aiohttp-based API server (ymir/api/) that
accepts POST requests to enqueue MR consolidation jobs into the
existing Redis hash queue.

- ymir/api/server.py: Generic API server with /healthz and pluggable
  route modules. Manages Redis lifecycle via redis_client().
- ymir/api/consolidation.py: POST /api/consolidation endpoint supporting
  auto mode (source_issues=null, dedup-safe) and label-triggered mode
  (source_issues set, returns 409 on conflict).
- Containerfile.api: Minimal Fedora 44 container image.
- compose.yaml: New 'api' service on port 8080 in agents profile.
- OpenShift manifests: Deployment, Service, Route, ImageStream.
- 13 unit tests covering both modes, validation, dedup, and errors.

Co-authored-by: Cursor <cursoragent@cursor.com>
Add a generic POST /api/jira/webhook endpoint that parses Jira
comment_created webhook payloads, detects bot mentions by account ID,
and dispatches commands via an extensible command registry.

- ymir/api/command_parser.py: Command registry with register() and
  dispatch(). New commands = one handler + one register() call.
- ymir/api/jira_webhook.py: Webhook endpoint with X-Webhook-Secret
  validation, comment_created filtering, and [~accountId:] mention
  detection.
- ymir/api/consolidation.py: Added handle_consolidate_command with
  argparse-based CLI parsing, registered as 'consolidate' command.
  Refactored shared _submit_consolidation_job() for reuse.
- ymir/api/app_keys.py: Extracted REDIS_KEY to break circular imports.
- Env vars: JIRA_WEBHOOK_SECRET, JIRA_BOT_ACCOUNT_ID added to
  compose.yaml and OpenShift deployment.
- 17 new unit tests (command parser + webhook), 30 total.

Co-authored-by: Cursor <cursoragent@cursor.com>
@TomasKorbar
TomasKorbar force-pushed the consolidation_triggering branch from e0072e2 to 4f41782 Compare September 9, 2026 07:58
@TomasKorbar

Copy link
Copy Markdown
Collaborator Author

It seems there are some missing pieces, here are some LLM findings:

1. **P1: Public unauthenticated job submission**
   `ymir/api/consolidation.py:80-97,146-148` exposes `/api/consolidation` without authentication through `openshift/route-api.yml`. Anyone reaching the route can enqueue costly arbitrary workflows. Additionally, webhook authentication fails open when `JIRA_WEBHOOK_SECRET` is unset (`ymir/api/jira_webhook.py:65-69`). Authentication should cover every mutating endpoint and fail closed.

Yeah there is no authentication, but the service is accessible only from internal network and ymir_todo can be placed by essentially anyone, so i am not sure whether implementing authentication is necessary/useful here.
The fail on missing JIRA_WEBHOOK_SECRET makes sense.

2. **P1: API image is never published**
   `openshift/imagestream-api.yml:10` imports `quay.io/jotnar/ymir-api:latest`, but `.github/workflows/build-and-push.yml` has no job publishing `Containerfile.api`. After merge, deployment will receive no new API image.

ah missed this. Did not know where to put it. Will fix.

3. **P1: Deployment requires an undocumented secret**
   `openshift/deployment-api.yml:50-51` references mandatory secret `api-env`, but `openshift/README.md:11-46` neither documents nor creates it. Unless provisioned out-of-band, the pod remains in `CreateContainerConfigError`.

Ah will add info to documentation.

4. **P2: None of the 30 API tests run in CI**
   `Makefile.tests:29-52` enumerates existing package test directories but omits `ymir/api/tests`. Current green checks therefore provide no test coverage for the new server.

Dammit.

5. **P2: `source_issues` accepts invalid cardinalities**
   `ymir/api/consolidation.py:31,109` accepts zero, one, or more than two issues, although consolidation operates on a pair. One issue queues a successful no-op; more than two populates all branches while `ymir/agents/prompts/mr_consolidation/prompt.j2:7-13` describes only the first two. Constrain this to exactly two entries when supplied.

Right. Makes sense.

- Fail closed when JIRA_WEBHOOK_SECRET is unset: return HTTP 500
  instead of skipping authentication entirely.
- Add build-and-push-api job to the CI workflow so the ymir-api
  image is actually published to quay.io.
- Document the api-env secret in openshift/README.md.
- Add check-api and check-api-in-container targets to Makefile.tests
  so the API tests run in CI.
- Constrain source_issues to exactly 2 entries via a Pydantic
  field_validator and tighten argparse nargs from '+' to 2.

Co-authored-by: Cursor <cursoragent@cursor.com>
@nforro

nforro commented Sep 9, 2026

Copy link
Copy Markdown
Member

Did not know where to put it. Will fix.

You also need to create the repo on quay.io.

@lbarcziova

Copy link
Copy Markdown
Member

thanks a lot for kickstarting this!

As for the current code, I have few findings with the help of Codex, worth double-checking:

  • /api/consolidation is exposed through a public OpenShift Route without authentication - the point already brought up by Nikola, I think adding the authentication doesn't hurt and if we want this to be right now triggered only via Jira, it seems like a more controlled approach (I think we do check for the group of the user who currently triggers via ymir_todo, for this we should also do similar user/group validation)
  • Jira webhook authentication uses the wrong protocol. Native Jira Cloud webhooks send an HMAC in X-Hub-Signature, calculated over the raw request body. The PR instead compares a raw X-Webhook-Secret value. Standard Jira Cloud deliveries will therefore not authenticate. See ymir/api/jira_webhook.py:26,64-75 and Atlassian’s webhook documentation (https://developer.atlassian.com/cloud/jira/platform/webhooks/#validating-webhook-deliveries).
  • Queue submission is still vulnerable to a race. The API checks for existing pending/active jobs and then performs a separate Redis submission. Two concurrent requests can both pass the check, with the later HSET overwriting the first source-specific job. See ymir/api/consolidation.py:53-70 and ymir/common/merge_queue.py:47-66.
  • Redis failures in the pre-check path are handled inconsistently. The source-specific HGETs happen outside the try block, so Redis failures bypass the intended JSON error response and propagate through the webhook handler differently from failures during submit_merge_job.
  • Readiness does not reflect Redis availability. /healthz always returns 200, and the same endpoint is used for readiness. If Redis becomes unavailable after startup, Kubernetes will continue routing
    requests to an API that cannot submit jobs.

I also wanted to bring up the Ymir triggering rearchitecture research here too (https://github.com/packit/research/blob/00a812858e100da0d7fb124d3b83220de990af19/research/ymir-triggering-rearchitecture/index.md) - the approach here uses webhooks, but I remember we were seriously considering also Automation + event router, with manual trigger forms. This choice affects authentication, payload format, authorization, deduplication, and UX. This would be worth re-descussing on arch. Considering that, we could split this PR into the API foundation and then the followup integration, wdyt? With that, the API foundation could still be tested standalone.

The initial research also showed the current OpenShift route may not be reachable from Jira directly, we would need to consider alternatives for that case: simple Ymir dashboard, polling, ..?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants