The list reached the bus only through a want of the bus module, which nobody writes (ADR 0539 §15): on 2026-10-25 it stood from 11:23 to 20:50 and was put back by hand. The node agent now follows its composition bucket for the newest signed composition carrying the user list, writes that file alone through the apply's file step when it differs (mode 0600, read back, recorded as the module's), moves nothing that reads it (the bus module's entrypoint reloads the server in place), never goes back to an older list, looks again after every apply of the bus module, and says source, revision, wanted and written digests, users and any error in the bus module's have (mesh-sdk UserListWritten). Read with mesh-delivery-view.node_haves (module nats); the bus's own nats_user_can says in_force once it reloaded.
68 lines
3.5 KiB
Go
68 lines
3.5 KiB
Go
package apply
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/declaration"
|
|
"github.com/novox/mesh-host/internal/store"
|
|
"github.com/novox/mesh-host/internal/system"
|
|
)
|
|
|
|
// WriteAlone writes one file a module's composition declares, through the apply's own file step, and records it as an
|
|
// apply would; nothing else of the module is applied, and nothing that reads the file is moved (novox/hq ADR 0547 §8).
|
|
//
|
|
// **For a file whose reader takes the change itself.** The bus's user list is the one it is for: the bus module's
|
|
// entrypoint watches the file and signals the server to reload in place, so the list is in force without a want of the
|
|
// bus, which nobody writes (ADR 0539 §15), and without the container being touched. A file that a service or a container
|
|
// must be moved to read is not written alone: it goes through the module's apply, which moves what reads it.
|
|
//
|
|
// The file is written only when its content differs from what is there (the file step's own rule), read back, and
|
|
// recorded under the module whose resource it is, so the module's next apply finds it as its own and writes nothing.
|
|
// The caller holds the apply's lock. log is said the path, the action and the digest, never the content.
|
|
func WriteAlone(ctx context.Context, sys system.System, file *declaration.File, known store.State, run Runner,
|
|
log func(string)) (Outcome, store.State, error) {
|
|
if log == nil {
|
|
log = func(string) {}
|
|
}
|
|
if file.Into != "" || file.CreateOnce || file.Sealed != "" || file.VaultSealed != nil || len(file.Secrets) > 0 {
|
|
return Outcome{}, known, &Error{Resource: file.ID, Err: fmt.Errorf("%s is not a whole file of plain content, "+
|
|
"and only such a file is written alone; nothing was written", file.Path)}
|
|
}
|
|
if file.IsReference() {
|
|
return Outcome{}, known, &Error{Resource: file.ID, Err: fmt.Errorf("%s is given by digest %s and was not "+
|
|
"resolved to its content; it is not written empty", file.Path, file.Digest)}
|
|
}
|
|
if held, isHeld := known.HeldAt(file.ID); isHeld {
|
|
return Outcome{}, known, &Error{Resource: file.ID, Err: fmt.Errorf("%s is held as found for a module not "+
|
|
"yet taken (%s), so nothing was written", file.Path, held.Why)}
|
|
}
|
|
was, _ := known.Find(file.ID)
|
|
if was.ID == "" {
|
|
// Only over a file the mesh wrote: a file this node holds no record of is the module's apply's to take, with
|
|
// the original kept, never this write's. A record of the path under another id (the declaration before the
|
|
// switch named it so) is the one written to, so no second record of the file is left for an apply to remove.
|
|
rec, ok := known.At(string(declaration.TypeFile), file.Path)
|
|
if !ok {
|
|
return Outcome{}, known, &Error{Resource: file.ID, Err: fmt.Errorf("this node holds no record of %s; it "+
|
|
"is written by the module's apply first, never alone", file.Path)}
|
|
}
|
|
renamed := *file
|
|
renamed.ID = rec.ID
|
|
file, was = &renamed, rec
|
|
}
|
|
in := inputs{declares: map[string]string{file.Identity(): declaredDigest(file)}, known: &known,
|
|
groups: groupsWanted([]declaration.Resource{file}), handed: Handed{}, dirsBefore: map[string]bool{},
|
|
files: map[string]bool{file.Path: true}}
|
|
outcome, err := applyOne(ctx, sys, file, run, map[string]bool{}, in, was, nil, nil)
|
|
if err != nil {
|
|
log(fmt.Sprintf(" failed %s (%s), alone: %v", file.ID, file.Path, err))
|
|
return outcome, known, &Error{Resource: file.ID, Err: err}
|
|
}
|
|
recordKeeping(&known, file, outcome, was)
|
|
if moved(outcome) {
|
|
log(fmt.Sprintf(" %s %s (%s), alone: %s", outcome.Action, file.ID, file.Path, outcome.wrote))
|
|
}
|
|
return outcome, known, nil
|
|
}
|