Skip to content

settings: every operator setting loads through one path - #38

Merged
github-actions[bot] merged 1 commit into
developfrom
feat/settings-load-through-one-path
Sep 12, 2026
Merged

github-actions[bot] merged 1 commit into
developfrom
feat/settings-load-through-one-path

Conversation

@traviswu-bigstack

Copy link
Copy Markdown
Contributor

What

configure(dir) walks a declared list of operator settings and returns the
configured registry; run calls it and reads no operator configuration itself.
The two shipped settings — the action level and the OpenStack credential — move
across unchanged.

ADR 0016 slice 1. No behaviour change: same files, same formats, same modes,
same messages, same handling of absent, empty, malformed and group-writable
input.

Why

Startup was a sequence of hand-written blocks, one per setting, and adding one
was optional. Two settings got their block. Two did not:

  • Registry.ConfigureInstanceProfile has no non-test caller, so a profile an
    operator writes reaches nothing and every create refuses with "this agent has
    no flavor configured"
    — a good message an operator cannot act on.
  • Registry.ConfigureCubeCOS is likewise test-only, so all twenty-one
    read-catalogue paths answer "not configured".

Both features are documented, exercised by tests, and absent from the shipped
binary. Lab validation on the 1cc R630 was the first thing to notice.

The reason every check passed is the part worth keeping: every test that found
them green built its own Registry
, so none of them ever asked whether
anything builds one in production. A test that does not run the startup path
cannot catch this, however thorough it is about everything else.

This slice does not fix either finding — slices 3 and 4 do. It makes them
unrepeatable, which is why it ships first.

Reviewer notes

Two test layers, and the second is not redundant.

TestEverySettingIsDeclaredAndAsserted requires the declared list and an
independently-written effect table to account for each other in both directions.
It catches a setting nothing loads.

TestEverySettingReachesTheRegistryThroughConfigure writes each setting's file
into a temporary directory, calls configure, and asserts the returned registry
differs from the unconfigured one in the way that setting controls. It catches
a setting loaded into nothing — which passes the first layer.

Both breaks were run and watched:

break result
credential removed from settings layer 1 fails: "setting "OpenStack credential" has an effect test but configure does not load it: an operator writing openstack-credential.json would change nothing" (layer 2 also fails)
credential declared and loaded, but its apply dropped layer 1 passes, layer 2 fails: "writing openstack-credential.json changed nothing … the setting is declared but wired to nothing"

The second row is why there are two layers.

Registry.Writers() is new and small. ConfigureWriter's observable
counterpart. Without it, a credential on disk reaching the registry is only
visible by making a call, so nothing could assert the wiring without a network —
which is how the two findings survived. It is also what the startup line can
grow into.

Per-setting masks stay per-setting. The level is refused if group- or
world-writable, the credential if group- or world-readable. ADR 0016 keeps a
directory of files rather than one config file precisely so these can disagree:
one is policy a customer should read without root, the other is a secret.

A broken setting disables what it enables and never more. The agent still
starts, and each setting prints one line — its value, "not configured", or the
reason it was rejected. settingState.broken exists for slice 2's
config check exit code and is asserted here.

Out of scope, noted once: -web-targets is a third operator-configured file
(console.LoadWebAllowlist), but it lives in a different directory and
configures the console handler rather than the registry, so it is not in ADR
0016's set. Worth deciding on when the next setting lands.

Verification. go test ./... exit 0, zero --- FAIL: lines, all packages
ok. go vet clean. gofmt clean on everything touched — cmd/agent/main_test.go
is unclean on develop already and is not part of this change.

Docs

kb/cube-ai-advisor/architecture/read-only-plane-end-to-end.md — the as-built,
beside the earlier slices.

Startup read operator configuration as a sequence of hand-written blocks,
one per setting, and adding one was optional. Two settings got their block —
the action level and the OpenStack credential. Two did not:
ConfigureInstanceProfile and ConfigureCubeCOS have no non-test caller, so a
profile an operator writes reaches nothing and all twenty-one read-catalogue
paths answer "not configured". Both features are documented, exercised by
tests, and absent from the shipped binary. Lab validation on the 1cc R630 was
the first thing to notice.

Every test that found them green built its own Registry, so none of them ever
asked whether anything builds one in production.

configure(dir) walks a declared list and returns the registry; run calls it and
reads no operator configuration itself. The two shipped settings move across
unchanged — same files, same formats, same modes, same messages, same
behaviour on absent, empty, malformed and group-writable input. Per-setting
masks stay per-setting: the level is refused if others may write it, the
credential if others may read it.

The tests call configure, in two layers. One requires the declared list and the
effect table to account for each other, catching a setting nothing loads. The
other writes each setting's file into a temporary directory and asserts the
returned registry changed, catching a setting loaded into nothing — which
passes the first. Registry.Writers reports which backends have a write client
wired, so a credential reaching the registry is observable without a network.

ADR 0016 slice 1.

Signed-off-by: Travis Wu <travis.wu@bigstack.co>
@traviswu-bigstack
traviswu-bigstack marked this pull request as ready for review September 12, 2026 04:24
@traviswu-bigstack traviswu-bigstack added the done Apply to a PR to trigger the fast-forward merge label Sep 12, 2026
@github-actions
github-actions Bot merged commit 635d2f2 into develop Sep 12, 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