[ConfigManager] Register Node Sections 1/4 - #3992
Draft
bdchatham wants to merge 2 commits into
Draft
Conversation
…ions The node's own configuration file carries 141 keys and no section declared any of them. These two are the first, and the registry needed two things before they could be. A section can now leave out a path the struct carries. Two kinds of field earn it. One a reader refuses outright, where writing the key stops the node, so declaring it would put a setting in the space whose only effect is an outage. And one whose absence is itself the setting, where a default would be this package inventing one. An exclusion naming no field the struct carries is refused, because the field it named can be renamed away and leave the exclusion reading as a deliberate omission while excluding nothing. A field that collects what the decode matched no field for now declares no key. What lands in it is what an operator misspelled, so giving it a key would offer the collector itself as a setting to write. Neither the package defining these settings nor the package deciding them can register them: the struct belongs to the node's configuration package and the rules that vary it by node kind live in the parameters package, which imports that struct. So a third package does it, the same shape the upstream server sections already use. Four keys vary by node kind and a test holds all four by value. Two are listen addresses, so a rule that stopped varying would leave a validator binding the address a query-serving node binds. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
The latest Buf updates on your PR. Results from workflow Buf / buf (pull_request).
|
Five of these sections carry a root directory field tagged the same as the key at the top of the file, and the node fills every one of them from the command line after the file is read. So the file never carries the value and each section states the empty string for it. Declaring it hands whatever delivers these values an empty root to write over a running node's, and a node that cannot find its data directory, its genesis file or its signing key does not start. Two of the five are here. The test checks every registered section rather than the five, so a section added later that carries the same field fails rather than shipping the same hole. 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.
What this does
The node's own configuration file carries 141 keys and no section declared any of
them. This declares the first two: the peer-to-peer table and the remote procedure
call table, 47 keys.
Nothing consumes them. A section reaches the registry through its owning package's
initialisation, and nothing imports the new package yet, so a node's configuration
reads exactly as it did.
Where the registration lives, and why not with the struct
Neither the package that defines these settings nor the package that decides them
can register them. The struct they decode into belongs to the node's configuration
package. The rules that vary them by node kind live in
app/params, which importsthat struct. The importing direction is already fixed, so only a third package sees
both, and
config/tendermintbaseis that package.This is the shape
config/cosmosbasealready uses for the upstream server'ssections, and it carries the same consequence: nothing imports it until a consumer
adds the import.
Two things the registry needed first
A section can now leave out a path its struct carries. Two kinds of field earn
it. One a reader refuses outright, where writing the key stops the node, so
declaring it would put a setting in the space whose only effect is an outage. And
one whose absence is itself the setting, where a default would be this package
inventing one.
An exclusion naming no field the struct carries is refused. The field it named can
be renamed away, and left alone the exclusion then excludes nothing while still
reading in the source as a deliberate omission.
A field that collects what the decode matched no field for now declares no key.
Such a field is tagged to absorb every name the decode did not match, so what lands
in it is what an operator misspelled. Giving it a key of its own would offer the
collector itself as a setting to write. No exclusion can reach it, because an
exclusion names a key and this field has none.
The paths left out here, and why each
The node's root directory. Five of these sections carry a field tagged the same as the key
at the top of the file, and the node fills every one of them from the command line after the
file is read. So the file never carries the value and each section states the empty string for
it. Declared, whatever delivers these values gets an empty root to write over a running node's,
and a node that cannot find its data directory, its genesis file or its signing key does not
start. Two of the five are here; the test walks every registered section rather than the five,
so a section added later carrying the same field fails instead of repeating it.
I did not find that by reading. A prior prototype of this work had a test named for exactly this
failure, and it was the first thing that test caught when I compared against it.
Two paths something outside this binary writes. The cluster's node controller resolves a
peer set from live discovery and patches the addresses into the node's own file. A value
declared here would be decoded over whatever already ran, with the change visible only in
memory and the file still saying otherwise. So
external-addressandpersistent-peersareleft out. The criterion is not that the binary fills them in — it is that something does, and
this file cannot see it.
One path that exists to make a node misbehave.
test-dial-failmakes every dial fail. Ithas no use on a real network and it is reachable from a file an operator edits by hand.
The one path left out for a reason of its own
p2p.max-outbound-connectionsis a pointer the node's defaults leave unset, andunset is what selects the behaviour: the node derives a ceiling from the total
connection limit instead. Any number written here would be a value no generated
file carries. A test fails if the node ever gives it a default, and the fix would
then be to declare it.
What varies by node kind
Four keys, and a test holds all four by value rather than describing them.
p2p.laddrtcp://127.0.0.1:26656tcp://127.0.0.1:26656tcp://0.0.0.0:26656rpc.laddrtcp://127.0.0.1:26657tcp://127.0.0.1:26657tcp://0.0.0.0:26657p2p.max-connectionsp2p.allow-duplicate-ipThe two listen addresses are the reason this is measured. A rule that stopped
varying would leave a validator binding the address a query-serving node binds, and
a validator holds a signing key. A comment saying otherwise cannot fail.
How the keys are derived
From the struct's own mapstructure tags, which is what the node decodes through, so
a declared key is a key that reader resolves rather than a second spelling of it. A
renamed tag moves the reader and the declaration together and there is no third
statement to drift from. The test asserts the count rather than the spellings for
that reason.
Verification
gofmt,goimports,go vetclean.golangci-lintreports nothing on thetouched packages.
config/...,cmd/seid/...andapp/paramspass.I also broke each new behaviour on purpose and checked a test objected: an
exclusion reaching the type walk but not the value walk, a stale exclusion being
accepted, and a collecting field declaring a key. All three were caught.
Reviewing this
This is the first of four, and they stack. Its base is a branch holding the four
section changes already in review, so the diff here shows only the new work. Once
those land, this retargets to the default branch.