Feature 57 (config and provisioning): modules declare config and provisions, their files rendered from templates; synced with main (issues of feature 57, 874)
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
# mesh-provisioner-client
|
||||
|
||||
Holds the `node-provisioner` seat on every node: the provisioner's client (novox/hq ADR 0349 clauses 3 to 5, design 57
|
||||
P5). Go, one binary (`cmd/mesh-provisioner-client`), run as a service of its own under the account
|
||||
`mesh-provisioner-client`, outside the tool runner, on a bus account of its own, `provisioner.<node>`, delivered as
|
||||
its own secret `provisioner-broker` (mesh-controller `internal/broker/provisioner.go`). The bus grants that account
|
||||
its own machine's keys and, to read alone, the whole assignments mirror, where a provider's consumers on any
|
||||
machine are bound to it (ADR 0373); nothing else.
|
||||
|
||||
## What it does
|
||||
|
||||
For each module the controller handed to the provisioner on its node — an assignment in
|
||||
`mesh-controller_assignments` (`<node>.<module>`) whose value carries a `provisioning` plan: the templates archive
|
||||
by reference and digest, the files it fills (path, template, `owner-at-runtime`, how its program takes a change)
|
||||
and the placement values its templates name — the client:
|
||||
|
||||
- fetches the templates archive and uses it only when it hashes to the plan's digest, keeping it by digest;
|
||||
- records each placeholder as a need in `mesh-provisioner_needs`, `<node>.<module>.<kind>.<name>` (a character a
|
||||
key cannot hold written `=<hex>`);
|
||||
- watches each owner's keys for its node: placement in the plan, config in `mesh-config_resolved`
|
||||
(`<node>.<module>.<key>`), both named by `mesh-provisioner_registry`;
|
||||
- asks the vault (`mesh-vault.get`, named by the registry) for each of the module's own secrets its plan names
|
||||
(`provisioning.secrets`), in its own name on the subject naming its account, with the public half of a key it
|
||||
made at start and keeps in memory alone; the vault answers the secret sealed to that key (ADR 0206's box), so only
|
||||
the box crosses the bus. A secret is asked once per run, and again when the vault says `secret-changed` for it,
|
||||
so a rotation reaches its file without a new declaration. A secret with a `path` is written to that file alone,
|
||||
as an `owned` file. A secret the vault does not hold yet, or a vault that does not answer, is waited for; a
|
||||
refusal fails the module with the vault's reason and is asked again after ten minutes. A value is never written
|
||||
to a bucket, a report or a log;
|
||||
- asks the vault the same way for each credential of what the module requires that its plan names
|
||||
(a `provisioning.provisions` entry with a `name`, issue 757; every entry names the provider serving that
|
||||
provision, issue 760, and its offer's credential form, issue 768, which the vault acts on): the value a provider's software issued for this module on this machine,
|
||||
`provision/<provider>/<provision>/<node>/<module>` (the forge's read token for claude-code, issue 737), filled
|
||||
wherever `${secret:<name>}` names it and written to its `path` alone, and asked again on its `secret-changed`;
|
||||
- fills each item the module contributes to a collection (`provisioning.items`: the collection and its template)
|
||||
from the same owners as a file, and records it in `mesh-provisioner_collect`, `<node>.<collection>.<contributor>`
|
||||
(a character a key's token cannot hold written `=<hex>`: `shell:zsh:normal` is `shell=3Azsh=3Anormal`). An item
|
||||
naming a secret fails: an item is kept in a bucket, and no secret is;
|
||||
- fills a collector's `${collected:<collection>}` from every item of that collection recorded for its node, in the
|
||||
contributors' order, each introduced by a line naming its contributor in the collection's comment
|
||||
(`provisioning.collections`: the collections the module collects, each with its comment). The three old
|
||||
namespaces read as collections of the same name: `${shell:zsh:normal}` is the collection `shell:zsh:normal`,
|
||||
`${environment:posix}` is `environment:posix`, `${contribution:node-hotkeys:trigger}` is
|
||||
`contribution:node-hotkeys:trigger`. A collector naming a collection its plan does not fails. A collection with no
|
||||
item is filled empty, and filled again whenever an item of it is recorded or goes;
|
||||
- fills a module's files only once every value is there; otherwise it writes nothing and reports `waiting` with what
|
||||
it waits for;
|
||||
- has the node-engine write each changed file through its local write socket (`/run/nox-mesh-host/fill.sock`,
|
||||
mesh-host `internal/filling`), which writes it as the declaration says (an `owned` file whole, a `seed` only when
|
||||
absent, a `managed` file's own keys or block) and restarts or reloads its program as the declaration says;
|
||||
- writes every file again every minute, so the node-engine puts back an `owned` file changed on disk;
|
||||
- reports `prov.applied.<node>.<module>` and keeps the same report in `mesh-provisioner_applied` (`<node>.<module>`):
|
||||
`applied` with each file's action and digest, `waiting`, `failed` with the reason, or `removed`;
|
||||
- when a module is taken off the node, removes its needs, its items and its report (the node-engine removes the files its
|
||||
declaration no longer names); and when the bus comes back, or the client starts, fills from the current values;
|
||||
- fills a provider's `${consumers:<provision>}` (novox/hq ADR 0373, issue 679) with one entry per consumer bound to
|
||||
it for that provision, in the order of the assignments' keys and, within a plan, of its `provisions` entries: a
|
||||
JSON list, each entry `{consumer, module, local, credential, value}`, the credential its own,
|
||||
`pair/<provider>/<provision>/<node>/<module>[/<local>]`, asked of the vault in the client's own name as a secret
|
||||
is. Who binds a provider is read from every machine's plans, so the client watches the whole assignments mirror
|
||||
(it carries no secret's value) and reads only its own machine's plans as its own. A consumer whose credential the
|
||||
vault does not hold yet, or will not hand, is left out and said in the log, so the others are served; the
|
||||
provider's helper waits for it (mesh-sdk `go/provider` ConsumersFile). It is filled again when a consumer binds or
|
||||
unbinds and when the vault says one of the credentials changed. The catalogue holds the file to mode 0600 and
|
||||
owned, and refuses the placeholder in a module not providing that provision (mesh-controller internal/catalogue
|
||||
consumers.go); an item naming it fails;
|
||||
- fills a consumer's `${bound:<provision>:<key>}` from its provider's entry (mesh-sdk `go/provider`, issue 678): the
|
||||
bucket `<provider module>_bound` of the provider its plan names, key `<node>.<module>.<provision>[.<local>]`, as
|
||||
the registry says, watched for its own machine's keys once a plan names it; `as`, `user` or one of the served
|
||||
values. An entry not written yet, or a bucket not there yet, is waited for; one the provider refused fails the
|
||||
module with the provider's reason.
|
||||
|
||||
It fills `config`, `secret` (design 57 V4, issue 647), the collections (`collected`, and `shell`, `environment`
|
||||
and `contribution` read as collections: design 57 L1, issue 684), `consumers` and `bound` (issue 679) and the
|
||||
placement kinds (`dir`, `access`, `port`, `seat`, `machine`). Any other kind fails with the reason.
|
||||
|
||||
## A module's code asks at runtime
|
||||
|
||||
A module whose code runs in its machine's tool runner reads its config values through the SDK (ADR 0349 clause 14
|
||||
as ADR 0370 changes it, issue 685). The tool runner asks this client on `prov.ask.<node>`, which only the same
|
||||
machine's tool runner may publish, with `{module, kind, key}`; the module is the one whose bundle asks, named by the
|
||||
tool runner and never by the module's code. The client answers `{value}`, the value as `mesh-config_resolved` holds
|
||||
it, only for a module assigned to its machine and a key mesh-config resolved for it (what its manifest declares), or
|
||||
`{error}` with the reason. It never answers a secret: the vault hands a secret only to a caller the bus proves, so a
|
||||
module in the tool runner reads its secrets from its files. A binding's non-secret values (`bound`) are filled
|
||||
into files (issue 679) and not yet answered on an ask. It says on `prov.changed.<node>.<module>` which value moved (`{kind, key}`, every
|
||||
key of the kind when `key` is absent, never the value), when the client starts and on every change, so the tool
|
||||
runner asks again for the watches it holds.
|
||||
Reference in New Issue
Block a user