Skip to content

toolplane: check every allowlist path against the API that must serve it - #33

Merged
github-actions[bot] merged 1 commit into
developfrom
fix/create-instance-matches-the-api
Sep 10, 2026
Merged

github-actions[bot] merged 1 commit into
developfrom
fix/create-instance-matches-the-api

Conversation

@traviswu-bigstack

Copy link
Copy Markdown
Contributor

What

Every allowlist Get/Post path is now checked against cube-cos-api's OpenAPI document on each test run, and create_instance's path is recorded as a named debt because the endpoint it names does not exist.

Why

Slice 3 of ADR 0011 shipped create_instance with this in its comment:

The path and body field names must be confirmed against the running cube-cos-api's OpenAPI document before this is enabled on a real cluster.

Confirmed. The answer is worse than "unverified":

POST /api/v1/datacenters/{dataCenter}/instances   — does not exist

Not in api/cube-cos-openapi/docs.yaml (submodule 09b7d76, via cube-cos-api 0458966), not in the api/docs.json embedded at build time, and not as a handler in that API's source. Its 102 paths cover nodes, images, volumes, settings, tunings, fixpacks, firmwares, grafana, events, healths and the rest — VM lifecycle is not among them. The only two paths containing instances are a GPU-card console and a Grafana dashboard link.

Control: the three shipped read paths — healths, nodes, events — are all present, so the method distinguishes a missing path from a bad search.

Because there is no endpoint, there is no request schema, so the Body field names cannot be confirmed either. CubeCOSPoster is documented as "an authenticated write against the local cube-cos-api", so the tool cannot reach whatever does own instances without a new transport.

This was safe only by accident of sequencing: no cluster has a level above observe and no writer is wired, so nothing could have called it. The moment either changed it would have been a failed write the model could not explain.

Reviewer notes

The entry stays. Everything slice 3 built around it — the level gate, the approval statement, the write ledger, the readable refusal — is correct and tested. What is missing is somewhere to send the request. Whoever supplies that decides the shape: an endpoint on cube-cos-api, or a transport reaching whatever owns instances. That is a design call, not a rename, so it is not made here.

Vendored, not read from a checkout. testdata/cube-cos-api-paths.txt holds the extracted path list with the revision it came from and the command that regenerates it. CI has no cube-cos-api checkout, and a test that skips when its input is missing reports green for exactly the case it was written to catch — the same argument #177 made for the corpus snapshot. specPaths also refuses a list under 50 paths, since a truncated file would make every path look absent and every exclusion look justified.

The exclusion list is kept honest in both directions. TestAnExcludedPathIsOneTheAPIReallyLacks fails if the API gains a path the list still calls missing — a debt someone paid, still recorded as owing. TestEveryExcludedPathIsStillInTheAllowlist fails if the list outlives the tool it describes.

{dc} vs {dataCenter} is the one rewrite between the two vocabularies, pinned as a constant rather than inlined, so a change to either is a failure and not a silent mismatch.

Proved failable — each break watched, then restored:

break failed
exclusion removed TestEveryAllowlistPathIsOneTheAPIServes "create_instance names …/instances, which cube-cos-api does not serve"
stale exclusion left listed TestEveryExcludedPathIsStillInTheAllowlist "no tool declares"
exclusion naming a path that exists TestAnExcludedPathIsOneTheAPIReallyLacks "the spec now has it"

go test ./... RC=0, all packages. go vet clean. gofmt -l internal/toolplane/ clean — note cmd/agent/main_test.go is gofmt-unclean on develop already and was left alone.

Docs

Handbook: the slice-3 as-built records the path as unverified; that sentence is being corrected in a separate handbook PR.

create_instance shipped naming POST /api/v1/datacenters/{dc}/instances.
cube-cos-api does not serve that path: it is absent from the OpenAPI
document, from the copy embedded at build time, and from that API's
source, whose resource families are nodes, images, volumes, settings,
tunings and the rest. VM lifecycle is not that API's concern, so the
body field names have no schema to be confirmed against either.

The slice-3 entry said the path "must be confirmed before this is
enabled on a real cluster". Confirming it is a thing a person does once
and then stops doing. This does it every run, against a vendored
extract of the spec rather than a checkout CI does not have, because a
test that skips when its input is missing reports green for the case it
was written to catch.

create_instance is listed as a named debt with its reason, and two
further tests keep that list honest: one fails if the API gains a path
the list still calls missing, the other if the list outlives the tool
it describes. The three shipped read paths pass unaided.

Signed-off-by: Travis Wu <travis.wu@bigstack.co>
@traviswu-bigstack
traviswu-bigstack marked this pull request as ready for review September 10, 2026 04:18
@traviswu-bigstack traviswu-bigstack added the done Apply to a PR to trigger the fast-forward merge label Sep 10, 2026
@github-actions
github-actions Bot merged commit e9b1337 into develop Sep 10, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

done Apply to a PR to trigger the fast-forward merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant