Skip to content

api: reject state/result changes to done nodes - #726

Merged
nuclearcat merged 2 commits into
kernelci:mainfrom
nuclearcat:node-done-immutable
Oct 29, 2026
Merged

nuclearcat merged 2 commits into
kernelci:mainfrom
nuclearcat:node-done-immutable

Conversation

@nuclearcat

@nuclearcat nuclearcat commented Oct 29, 2026 •

Copy link
Copy Markdown
Member

Problem

A lab can report the same job more than once. On production, baseline-arm64-sasha-lab on jetson-tx1-devkit (node 6ae228345c0836a8c2f041a8) was submitted as done/pass at 17:15 and as done/fail (kernel panic) at 17:29 on 2026-10-28. The second PUT /nodes replaced the first result. The KCIDB bridge had already sent the PASS, so the dashboard still shows PASS while Maestro shows fail. Nothing reported the mismatch; it only turned up in the dashboard validation workflow.

The cause: the state machine allows done → done, so a done node's result can change freely, and PUT /nodes/{id} replaces the root node without any transition check.

Change

  • PUT /node/{id}, PATCH /node/{id} and PUT /nodes/{id} return 409 Conflict if they would change the state or result of a node that is already done. The first final result wins. For PUT /nodes, every existing node in the hierarchy is checked before anything is written.
  • Still allowed:
    • resubmitting the same result (e.g. adding data or child nodes)
    • the late result for a node closed without one (incomplete or no result, e.g. by node_timeout), which the LAVA callback relies on
  • Admins can override with ?force=true. For other users, force is ignored.
  • Every rejected or forced change:
    • increments node_done_update_rejected_total / node_done_update_forced_total
    • publishes an event on the new node-integrity channel, with node, runtime, platform, old and attempted state/result, and user. Events are stored in eventhistory, so they can be queried with /events?channel=node-integrity. kernelci-pipeline's dashboard validation workflow posts them to Discord (ci: Report rejected changes to done nodes kernelci-pipeline#1663).
    • A failed publish never turns the 409 into a 500.

A separate commit fixes the unit-test auth mocks. They were registered on app only, while requests go to versioned_app, so every authenticated endpoint test returned 401.

Testing

Unit tests were run in the docker/api/Dockerfile image (tests requirements, kernelci-core main) against redis 6.2 and mongo 5.0:

  • 8 new tests in tests/unit_tests/test_node_done_update.py pass.
  • The override fix makes 7 existing tests pass.
  • Otherwise the suite fails exactly as it does on main in this environment: the same 15 failures and 8 errors, all in tests this change doesn't touch.

Deployment notes

  • Pipeline services that update a done node with a different result now get a 409. src/timeout.py already catches HTTPError. Each 409 is also visible on the node-integrity channel.
  • With "first result wins", a later report can be the correct one, as in the example above. Each alert needs a manual check, and an admin can correct the node with force=true.

The unit tests send requests to versioned_app, which gets the API routes
through include_router() and resolves dependencies against its own
dependency_overrides. The auth mocks were only registered on app, so
every authenticated endpoint test got 401 Unauthorized.

Register the same overrides on versioned_app.

Signed-off-by: Denys Fedoryshchenko <denys.f@collabora.com>
A lab can report a job more than once. The later report silently
replaced the first result of a node that was already done (e.g. pass,
then fail 15 minutes later), while the KCIDB bridge had already sent the
first result to the dashboard, leaving Maestro and the dashboard out of
sync with nobody noticing.

Make the first final result win: PUT/PATCH /node and PUT /nodes now
return 409 Conflict when they would change the state or result of a done
node. The hierarchy is checked as a whole before anything is written.
Resubmitting the same result is still allowed, and so is the late result
for a node closed without one (incomplete, e.g. by node_timeout), which
the LAVA callback relies on. Admins can override with force=true.

Every rejected or forced change is counted in the metrics and published
on the node-integrity channel, so it can be monitored through
/events?channel=node-integrity.

Signed-off-by: Denys Fedoryshchenko <denys.f@collabora.com>
@nuclearcat nuclearcat changed the title Node done immutable api: reject state/result changes to done nodes Oct 29, 2026
@nuclearcat
nuclearcat added this pull request to the merge queue Oct 29, 2026
Merged via the queue into kernelci:main with commit 899d90f Oct 29, 2026
3 checks passed
@nuclearcat
nuclearcat deleted the node-done-immutable branch October 29, 2026 12:47
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.

1 participant