ADR 0262: a module with a field the controller does not know is left out and raised, never read silently
Dropping an unknown key would run a module without something its manifest says. Also name the check by what a key is about, narrow the listing to one machine, and say how to roll back below this record.
This commit is contained in:
+32
-16
@@ -90,7 +90,9 @@ the mesh, or the node. Where the mesh or the node overrides a default, the defau
|
||||
**One listing of every preference, in the controller's `settings` verb.** Asked with `list:
|
||||
"preferences"`, or with no module, `settings` lists every module that declares a preference: each key,
|
||||
its default and why, and, for every machine the module is assigned to, the value there and where it
|
||||
comes from (the default, the mesh, or the node). A module and a node narrow it. No module builds a
|
||||
comes from (the default, the mesh, or the node). A module and a node narrow it: with a node, only the
|
||||
modules on that machine are listed, and a machine the mesh does not know is refused. Values or a clear
|
||||
without a module are refused: a module is needed to set values. No module builds a
|
||||
settings tool of its own; this verb is the interface for all of them, so a person or an agent finds every
|
||||
preference in one place.
|
||||
|
||||
@@ -101,12 +103,24 @@ for is refused, because a default that reaches nothing is the same mistake as a
|
||||
and a node may be called anything, `default` included. The defaults are told apart by a mark on the
|
||||
layer, never by its name.
|
||||
|
||||
**A stored manifest is read leniently; a manifest being registered is read strictly.** Registration
|
||||
and the module check refuse a key the controller does not know, as before. Reading the catalogue the
|
||||
store already holds sets such a key aside, says so once in the controller's log, and keeps the rest.
|
||||
Without this, the first manifest to use `settings` would make every older controller fail to read the
|
||||
whole catalogue, and with it every plan and every send on every node, the moment the controller was
|
||||
rolled back. The same holds for every field a later record adds.
|
||||
**A stored manifest with a key the controller does not know costs that module, never the catalogue,
|
||||
and never silently.** Registration and the module check refuse such a key, as before. A manifest the
|
||||
store already holds was registered by a newer controller and is read by this one after a rollback.
|
||||
Read strictly, it failed the whole catalogue, and with it every plan and every send on every node.
|
||||
Read with the key dropped, the module would run without something its manifest says, which is worse.
|
||||
So the controller reads the rest of the catalogue as before, and for that module:
|
||||
|
||||
- it is left out of every machine's declaration, by name, with the key as the reason, the way a module
|
||||
whose stored settings no longer compose is left out
|
||||
([ADR 0163](0163-taking-a-module-over-is-a-comparison.md), rule 6); what the machine holds for it is kept and its
|
||||
containers are untouched;
|
||||
- the controller raises a condition naming the module, saying it uses a field this controller does not
|
||||
know and is left out until the controller is updated, and clears it once the module reads again;
|
||||
- the controller's log says it once.
|
||||
|
||||
This holds at any depth: a key inside a block (a state, a provided name, an offer's identity, a data
|
||||
item's backup, an own secret, a seat's verb) is treated the same as one at the top. It holds for every
|
||||
field a later record adds, not only `settings`.
|
||||
|
||||
## Consequences
|
||||
|
||||
@@ -115,11 +129,12 @@ rolled back. The same holds for every field a later record adds.
|
||||
- A manifest that declares `settings` is refused by a controller older than this record, which reads
|
||||
manifests strictly. The controller change is sent first, and a catalogue change that uses the field
|
||||
merges only after it is live.
|
||||
- **The lenient read protects rollbacks to this controller and later ones, not to earlier ones.** Once a
|
||||
manifest in the store uses `settings`, rolling the controller back to a version before this record
|
||||
still fails the catalogue read. Rolled back to this version or a later one, the module's defaults are
|
||||
set aside with the field, so a module that relies on a default is left out of its machine's
|
||||
declaration by name, and the rest of the machine is composed.
|
||||
- **This protects rollbacks to this controller and later ones, not to earlier ones.** Once a manifest
|
||||
in the store uses `settings`, a controller from before this record still fails the whole catalogue
|
||||
read. **Before rolling the controller back below this record, register the notifier again at its
|
||||
manifest from before this record**, which has no `settings`; then roll back. Rolled back to this
|
||||
version or a later one, nothing needs doing first: the notifier is left out of its machines by name,
|
||||
a condition says so, and every other module is composed.
|
||||
- **What got harder:** a module author now decides, for every `${setting:…}`, whether the value is a
|
||||
preference or the operator's own, and says so. The check below catches the clear mistakes by the
|
||||
key's name and by the value naming an installation. It cannot catch a preference default that is
|
||||
@@ -136,18 +151,19 @@ rolled back. The same holds for every field a later record adds.
|
||||
| Rule | Checked by |
|
||||
|---|---|
|
||||
| A default is declared only for a preference, with why | The controller's manifest parser, run on every catalogue pull request as the module check: a `settings` entry is refused without `kind: preference`, without a default, with an empty or non-scalar default, or without `why` (`TestTheParserTakesAPreferenceAndRefusesTheRest`) |
|
||||
| The operator's own value has no default | The same parser refuses a default for a key that has, as a whole word, a name of the operator's own value (among them domain, host, zone, url, ip, mail, address, user, owner, uid, password, secret, token, key, and client-id), and the installation check of ADR 0155 already refuses a default naming a domain or a public address. `TestAnOperatorsValueWithoutADefaultIsStillRefused`: an unset key with no default is still refused by name |
|
||||
| The operator's own value has no default | The same parser refuses a default for a key about the operator's own value: its last word, read as singular, is one such name (among them domain, host, hostname, zone, site, timezone, url, ip, ipv4, ipv6, mail, address, user, owner, uid, password, secret, token, key, apikey, bearer, cert), or its last two are a compound such as client-id, site-name, server-name, public-name or smtp-relay. A key that starts with max, min, show, hide, enable or use is a number or a switch, not an identity, and the installation check of ADR 0155 already refuses a default naming a domain or a public address. `TestAnOperatorsValueWithoutADefaultIsStillRefused`: an unset key with no default is still refused by name |
|
||||
| The node over the mesh over the default | `TestTheNodeOverTheMeshOverTheDefault`, and `TestAComposedMachineGetsTheDefaultAndTheNodesValue` through a machine's whole composition |
|
||||
| A default never becomes a key of a mergeable file | `TestADefaultIsNotMergedIntoAJSONFile` |
|
||||
| A key with a default is accepted by `settings` | `TestAKeyWithADefaultIsNotStray` |
|
||||
| A default reaches something | The parser refuses a declared key that nothing asks for (`TestTheParserTakesAPreferenceAndRefusesTheRest`, "read by nothing") |
|
||||
| Every value says where it came from | `TestEveryValueSaysWhereItCameFrom` and `TestSettingsSayWhereEachValueComesFrom` |
|
||||
| The operator's word is a whole word of the key | `TestAKeyIsTheOperatorsByAWholeWord`: `max-tokens`, `show-hostname`, `users-per-page`, `mailbox-size` are preferences; `api-key`, `oauth-client-id`, `public-ip`, `dns-zone` are not |
|
||||
| A key is the operator's by what it is about | `TestAKeyIsTheOperatorsByWhatItIsAbout`: `max-tokens`, `show-hostname`, `users-per-page`, `mailbox-size`, `user-agent`, `url-timeout` are preferences; `api-key`, `oauth-client-id`, `public-ip`, `dns-zone`, `allowed-hosts`, `site-name`, `smtp-relay` are not |
|
||||
| A default is read by the installation check | `TestTheInstallationCheckReadsADefault`, including a setting called `description` |
|
||||
| A default fills a contribution and a served fact, and never replaces a contribution's own value | `TestADefaultFillsAContributionAndAServedFactButNotALiteral` |
|
||||
| A node called `default` is a node | `TestANodeCalledDefaultIsANodesLayer` |
|
||||
| One listing of every preference, per machine with its source | `TestSettingsListPreferences` (the verb's arguments, a listing other than preferences refused) and `TestPreferencesSayEachMachinesValueAndItsSource`; live, `mesh-controller.settings {list: "preferences"}` lists the notifier on both machines |
|
||||
| A stored manifest with an unknown key is read, and registration refuses it | `TestAStoredManifestWithAnUnknownKeyIsReadAndRegistrationRefusesIt` |
|
||||
| One listing of every preference, per machine with its source | `TestSettingsListPreferences` (the verb's arguments; a listing other than preferences, and values or a clear without a module, refused), `TestPreferencesSayEachMachinesValueAndItsSource`, and `TestPreferencesListedFromTheStores` (a machine names only the modules on it; a machine the mesh does not know is refused); live, `mesh-controller.settings {list: "preferences"}` lists the notifier on both machines |
|
||||
| A stored manifest with an unknown key, at the top or in any block, leaves its module out by name, and registration refuses it | `TestAStoredManifestWithAnUnknownKeyIsLeftOutAndRegistrationRefusesIt` (one case per block); `TestTheCatalogueLoadsAroundAManifestWithAnUnknownKey` over the store |
|
||||
| It is a condition until it reads again | `TestAModuleWithAnUnknownFieldIsACondition`: raised in plain words, cleared once the module reads |
|
||||
| Nothing changes where no machine sets a value | The notifier's manifest test fills the file with the defaults and finds today's lines; live, `mesh-controller.plan` for the workstation shows no change to the notifier's file after the catalogue change is sent |
|
||||
|
||||
## References
|
||||
|
||||
Reference in New Issue
Block a user