Compose a module's Go service as a process the host runs (hq issue 213)
The controller is to be declared as a Go bundle run by a process instead of an image (novox/hq issue 213, ADR 0188 §1, §3). The composer could not express that honestly yet: - a module declaring tools had every bundle served by the node's runtime, so the controller's own binary would have been launched a second time as an MCP child; a bundle one of the module's resources runs is now served only when it says `loads` - a module's accounts went after the mesh-computed files, so secrets owned by the account a process runs as were refused on the first apply; a module's `user` resources now go first - `prepares` derived its step only from a container; a process is now prepared by the same program with `prepare` as a run-once process - a process may say what it `replaces` (a resource of its module it no longer declares), prefixed as the host records it, so the host keeps the old one running until the process is (needs mesh-host's `replaces`) This lands before the controller's manifest uses any of it: the running controller composes its own declaration, so the code that fills the new shape must be live first.
This commit is contained in:
@@ -1515,6 +1515,53 @@ func ParseManifest(raw []byte) (Manifest, error) {
|
||||
"program that reads what the mesh delivered and reconciles",
|
||||
m.Module, r["id"]))
|
||||
}
|
||||
// **What a process replaces is something the module no longer declares** (novox/hq issue 213).
|
||||
// The host keeps it running until the process is, then removes it: so it is named by the id the
|
||||
// module used to give it, it is never a resource the module still declares — that would be
|
||||
// applied and removed by one declaration — and only a process that stays up has anything to
|
||||
// hand over to. Said here, near the author, as the host would refuse it far away.
|
||||
ids := map[string]bool{}
|
||||
for _, r := range m.Resources {
|
||||
ids[fmt.Sprint(r["id"])] = true
|
||||
}
|
||||
for _, r := range m.Resources {
|
||||
raw, present := r["replaces"]
|
||||
if !present {
|
||||
continue
|
||||
}
|
||||
if fmt.Sprint(r["type"]) != "process" {
|
||||
problems = append(problems, fmt.Sprintf(
|
||||
"%s: %v says what it replaces, and only a process does", m.Module, r["id"]))
|
||||
continue
|
||||
}
|
||||
if once, _ := r["run-once"].(bool); once || r["schedule"] != nil {
|
||||
problems = append(problems, fmt.Sprintf(
|
||||
"%s: %v replaces something and runs once or on a schedule — only a process that stays "+
|
||||
"up is there a moment later to hand over to", m.Module, r["id"]))
|
||||
}
|
||||
list, ok := raw.([]any)
|
||||
if !ok {
|
||||
problems = append(problems, fmt.Sprintf(
|
||||
"%s: %v replaces %v; replaces is a list of the ids this module no longer declares",
|
||||
m.Module, r["id"], raw))
|
||||
continue
|
||||
}
|
||||
for _, item := range list {
|
||||
id, ok := item.(string)
|
||||
switch {
|
||||
case !ok || strings.TrimSpace(id) == "":
|
||||
problems = append(problems, fmt.Sprintf(
|
||||
"%s: %v replaces %v, which is not an id", m.Module, r["id"], item))
|
||||
case strings.Contains(id, "."):
|
||||
problems = append(problems, fmt.Sprintf(
|
||||
"%s: %v replaces %q; a process replaces only a resource of its own module, named "+
|
||||
"by its own id", m.Module, r["id"], id))
|
||||
case ids[id]:
|
||||
problems = append(problems, fmt.Sprintf(
|
||||
"%s: %v replaces %q, which this module still declares", m.Module, r["id"], id))
|
||||
}
|
||||
}
|
||||
}
|
||||
// **A module that prepares its state must have code the mesh can run** (novox/hq ADR 0135). The
|
||||
// preparation is the module's own program in its preparation mode, so it is derived from the
|
||||
// resource that runs that program — and a module declaring none has asked for something the mesh
|
||||
@@ -1522,7 +1569,7 @@ func ParseManifest(raw []byte) (Manifest, error) {
|
||||
// quietly prepares nothing.
|
||||
if m.Prepares && preparationTarget(m) == "" {
|
||||
problems = append(problems, fmt.Sprintf(
|
||||
"%s says it prepares its state, and declares no container running an artifact it built — "+
|
||||
"%s says it prepares its state, and declares no container or process running an artifact it built — "+
|
||||
"the preparation is this module's own program, so there has to be one for the mesh to "+
|
||||
"run it in", m.Module))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user