fix: validate environment names, and read the owner record one way - #73
Merged
Conversation
Two defects found by review of the merged #71, both created by it. That change made the environment name load-bearing: bootstrap writes it into the host owner record and every later command parses it back through a strict grammar. Nothing validated it. `environments:` was the one name-bearing map in validate.go whose key was never checked against gIdent, which was harmless while the name was a lookup key and nothing else. So `Staging`, `prod_east` and `staging replica` all passed `ob validate`, and all three are rejected by the parser that reads the record back. Bootstrap writes the record and succeeds; every mutation afterwards refuses a record it cannot parse, permanently, with no remedy in the message. A legal config bricked the host. This is the item filed as #54 and downgraded to low earlier the same day, on the grounds that the name no longer reached a shell or a filename. That was true when it was written and stopped being true a few hours later, in this repository, without the issue being revisited. Preflight also disagreed with the engine. It read the first two fields of the record and ignored anything after them, so a three-field record passed preflight and then failed every mutation on the engine's parser. A check that is more permissive than the thing it predicts is worse than no check, so there is now one parser — app.ParseHostOwnerRecord — used by both. The refusal for an unparseable record now names its remedy, matching the empty record beside it: no ob command rewrites a record it cannot read, so the operator has to be told to remove it. Tests cover both, and both fail without the fix. Removing the validator gate: --- FAIL: TestEnvironmentNamesMustSurviveTheOwnerRecord environment name "Staging" was accepted by the loader but cannot round-trip the owner record `just check`, `golangci-lint run ./...` and `go test -race ./...` pass. Closes #54. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two defects found by an independent review of the merged #71 — both introduced by it. Closes #54.
1. A legal config could brick the host
#71 made the environment name load-bearing:
bootstrapwrites it into the host owner record, and every later command parses it back through a strict grammar. Nothing validated it —environments:was the one name-bearing map invalidate.gowhose key was never checked againstgIdent.Measured:
ob bootstrapwrites the record and succeeds. Every mutation afterwards refuses a record it cannot parse — permanently, and the message named no remedy.Worth stating plainly: this is #54, which I filed this morning and then downgraded to low after #51, on the grounds the name no longer reached a shell or filename. That was true when written and stopped being true a few hours later, in this repository, without the issue being revisited.
2. Preflight was more permissive than the engine
hostOwnerCheckreadfields[0]andfields[1]and ignored the rest; the engine rejects anything that is not exactly 1 or 2 fields. A three-field record got a green preflight and then failed every mutation — the inversion preflight exists to prevent.There is now one parser,
app.ParseHostOwnerRecord, used by both.3. The unparseable-record refusal names a remedy
Matching the empty-record case beside it. No
obcommand rewrites a record it cannot read, so the operator has to be told to remove it.Tests
Both regressions covered, both fail without the fix:
The existing
TestHostOwnerInvalidRecordFailsClosedasserted on the word "invalid"; it now asserts the refusal names the record and its remedy, which is the property that matters.Verified
just check,golangci-lint run ./...,go test -race ./....