Merge branch 'b6/registry' into feat/registry-milestone
This commit is contained in:
@@ -280,7 +280,7 @@ func ownPackages(t *testing.T) map[string][]string {
|
||||
}
|
||||
|
||||
// 1621/AC2 and AC4: the command imports, through every package of this repository it reaches, only the standard
|
||||
// library, mesh-sdk, the private network's overlay package (mesh-wireguard's own pure package: names, graph and
|
||||
// library, mesh-sdk, the contracts modules' pure packages (seats' types, clients' codecs), the private network's overlay package (mesh-wireguard's own pure package: names, graph and
|
||||
// generator, no bus) and this repository's own packages, and none that reaches a bus, a store, a network service or
|
||||
// another process.
|
||||
func TestTheCommandImportsNoBusStoreNetworkOrProcess(t *testing.T) {
|
||||
@@ -292,7 +292,7 @@ func TestTheCommandImportsNoBusStoreNetworkOrProcess(t *testing.T) {
|
||||
for path, imports := range own {
|
||||
for _, imp := range imports {
|
||||
std := !strings.Contains(strings.SplitN(imp, "/", 2)[0], ".")
|
||||
if !std && !strings.HasPrefix(imp, "git.novox.be/novox/mesh-networking/modules/mesh-wireguard/go/overlay") && !strings.HasPrefix(imp, "git.novox.be/novox/mesh-sdk/go") && !strings.HasPrefix(imp, "github.com/novox/mesh-host/") {
|
||||
if !std && !strings.HasPrefix(imp, "git.novox.be/novox/mesh-networking/modules/mesh-wireguard/go/overlay") && !strings.HasPrefix(imp, "git.novox.be/novox/mesh-sdk/go") && !strings.HasPrefix(imp, "git.novox.be/novox/mesh-contracts/") && !strings.HasPrefix(imp, "git.novox.be/novox/mesh-delivery/contracts/") && !strings.HasPrefix(imp, "github.com/novox/mesh-host/") {
|
||||
t.Errorf("%s imports %s, which is neither the standard library, mesh-sdk nor this repository", path, imp)
|
||||
}
|
||||
for _, f := range forbidden {
|
||||
|
||||
@@ -2,12 +2,12 @@ package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
engcomp "github.com/novox/mesh-host/internal/compositions"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// authorityPath is where this engine keeps the mesh's own certificate as it last applied it (public): beside the
|
||||
@@ -17,7 +17,7 @@ func authorityPath(state string) string { return filepath.Join(filepath.Dir(stat
|
||||
// keepAuthority keeps the mesh's own certificate a composition carries (the resource a module's certificate.authority
|
||||
// asks for, catalogue.AuthorityID), so the record says the authority this machine holds. A composition carrying none
|
||||
// changes nothing.
|
||||
func keepAuthority(state string, c delivery.Composed) error {
|
||||
func keepAuthority(state string, c engcomp.Composed) error {
|
||||
for _, raw := range c.Resources {
|
||||
var r struct {
|
||||
ID string `json:"id"`
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
engcomp "github.com/novox/mesh-host/internal/compositions"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -29,13 +30,13 @@ func TestTheAppliedAuthorityIsKeptForTheRecord(t *testing.T) {
|
||||
raw, _ := json.Marshal(map[string]any{"id": id, "type": "file", "path": "/etc/x", "content": content})
|
||||
return raw
|
||||
}
|
||||
if err := keepAuthority(state, delivery.Composed{Resources: []json.RawMessage{res("web.conf", "x")}}); err != nil {
|
||||
if err := keepAuthority(state, engcomp.Composed{Resources: []json.RawMessage{res("web.conf", "x")}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := os.Stat(authorityPath(state)); !os.IsNotExist(err) {
|
||||
t.Fatalf("a composition with no authority kept one: %v", err)
|
||||
}
|
||||
if err := keepAuthority(state, delivery.Composed{Resources: []json.RawMessage{res("ca-trust.certificate-authority", "CA-1")}}); err != nil {
|
||||
if err := keepAuthority(state, engcomp.Composed{Resources: []json.RawMessage{res("ca-trust.certificate-authority", "CA-1")}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := recordKeys(state, nil)(); got.Authority != "CA-1" {
|
||||
|
||||
@@ -2,10 +2,9 @@ package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
engcomp "github.com/novox/mesh-host/internal/compositions"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
// A composition that names a file the provisioner fills without saying it is filled would have this node-engine write
|
||||
@@ -13,7 +12,7 @@ import (
|
||||
// when ace, g14 and shanks wrote claude-code's, node-env's and power's files empty).
|
||||
func TestAFileItCannotProduceIsNeverWrittenEmpty(t *testing.T) {
|
||||
unit := json.RawMessage(`{"id":"power.after-boot-unit","type":"file","path":"/etc/systemd/system/mesh-power-after-boot.service","mode":"0644","owner-at-runtime":"owned"}`)
|
||||
c := delivery.Composed{Node: "ace", Resources: []json.RawMessage{unit}}
|
||||
c := engcomp.Composed{Node: "ace", Resources: []json.RawMessage{unit}}
|
||||
d, err := declarationOf(c)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -25,7 +24,7 @@ func TestAFileItCannotProduceIsNeverWrittenEmpty(t *testing.T) {
|
||||
if d, _ = declarationOf(c); refuseEmptyWrites(d) != nil {
|
||||
t.Fatalf("a filled file was refused: %v", refuseEmptyWrites(d))
|
||||
}
|
||||
plain := delivery.Composed{Node: "ace", Resources: []json.RawMessage{json.RawMessage(
|
||||
plain := engcomp.Composed{Node: "ace", Resources: []json.RawMessage{json.RawMessage(
|
||||
`{"id":"x.f","type":"file","path":"/etc/x","mode":"0644","content":"x"}`)}}
|
||||
if d, _ = declarationOf(plain); refuseEmptyWrites(d) != nil {
|
||||
t.Fatalf("a file with its content was refused: %v", refuseEmptyWrites(d))
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
engcomp "github.com/novox/mesh-host/internal/compositions"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
@@ -11,7 +12,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/declaration"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"github.com/nats-io/nats.go"
|
||||
"github.com/novox/mesh-host/internal/link"
|
||||
)
|
||||
@@ -140,7 +140,7 @@ func TestADeclarationWithAFileByDigestAppliesFromTheObject(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
runs := runsAsWritten([]delivery.Composed{{Node: "n", Module: "m", Resources: []json.RawMessage{stale}}}, opts.state)
|
||||
runs := runsAsWritten([]engcomp.Composed{{Node: "n", Module: "m", Resources: []json.RawMessage{stale}}}, opts.state)
|
||||
var got struct {
|
||||
Digest string `json:"digest"`
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
engcomp "github.com/novox/mesh-host/internal/compositions"
|
||||
"time"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"github.com/novox/mesh-host/internal/apply"
|
||||
"github.com/novox/mesh-host/internal/liveness"
|
||||
)
|
||||
@@ -26,8 +26,8 @@ func heldBy(windows *apply.Windows) func(time.Time) map[string]bool {
|
||||
// long-running resource the composition declares running not active, read from the service manager and the container
|
||||
// runtime and from no record. A resource held by an open maintenance window, or waiting for a library, is not counted;
|
||||
// one that cannot be read is not counted either (unknown is not inactive), and neither is one the manager is restarting.
|
||||
func inactiveIn(rt liveness.Runtime, held func(time.Time) map[string]bool) func(context.Context, delivery.Composed) bool {
|
||||
return func(ctx context.Context, c delivery.Composed) bool {
|
||||
func inactiveIn(rt liveness.Runtime, held func(time.Time) map[string]bool) func(context.Context, engcomp.Composed) bool {
|
||||
return func(ctx context.Context, c engcomp.Composed) bool {
|
||||
d, err := declarationOf(c)
|
||||
if err != nil {
|
||||
return false
|
||||
|
||||
@@ -4,9 +4,9 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
engcomp "github.com/novox/mesh-host/internal/compositions"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"github.com/novox/mesh-host/internal/liveness"
|
||||
)
|
||||
|
||||
@@ -77,7 +77,7 @@ func testTheMachineShowsAResourceNotActive(t *testing.T) {
|
||||
{"unread", fakeRuntime{err: errors.New("the service manager could not be read")}, nil, false},
|
||||
} {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
d, err := declarationOf(delivery.Composed{Node: "n1", Module: "dnsmasq", Resources: []json.RawMessage{svc, ctr}})
|
||||
d, err := declarationOf(engcomp.Composed{Node: "n1", Module: "dnsmasq", Resources: []json.RawMessage{svc, ctr}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
+21
-20
@@ -6,6 +6,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
nodeengine "git.novox.be/novox/mesh-contracts/go/seats/nodeengine"
|
||||
"github.com/novox/mesh-host/internal/compositions"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -256,16 +257,16 @@ func awaitBackup(ctx context.Context, events <-chan []byte, node, module string,
|
||||
// the controller put modules it knew no fingerprint of the new form for into the node's own composition while their
|
||||
// wants named their own, and the declaration joined of both declared each such file twice, which the filling refuses
|
||||
// ("2 resources declare X") on every node. The node's own copy is left out, and which were left out is said once.
|
||||
func declarationOf(cs ...delivery.Composed) (*declaration.Declaration, error) {
|
||||
func declarationOf(cs ...compositions.Composed) (*declaration.Declaration, error) {
|
||||
d := &declaration.Declaration{Version: declaration.Version}
|
||||
var modules []delivery.Composed
|
||||
var modules []compositions.Composed
|
||||
for _, c := range cs {
|
||||
if c.Module != "" {
|
||||
modules = append(modules, c)
|
||||
}
|
||||
}
|
||||
if len(modules) > 0 {
|
||||
joined := make([]delivery.Composed, 0, len(cs))
|
||||
joined := make([]compositions.Composed, 0, len(cs))
|
||||
for _, c := range cs {
|
||||
if c.Module == "" {
|
||||
var dropped []string
|
||||
@@ -300,7 +301,7 @@ func declarationOf(cs ...delivery.Composed) (*declaration.Declaration, error) {
|
||||
// the same kind and id. A resource that shares only its target (a unit or container of the same name, another id) is
|
||||
// not the module's, and stays: dropped, its record would be an orphan the apply removes, stopping the module's unit. It
|
||||
// answers what it left out, as "<id> (<module>)".
|
||||
func ownWithout(own delivery.Composed, modules []delivery.Composed) (delivery.Composed, []string) {
|
||||
func ownWithout(own compositions.Composed, modules []compositions.Composed) (compositions.Composed, []string) {
|
||||
byID := map[string]string{}
|
||||
for _, c := range modules {
|
||||
if c.Module == "" {
|
||||
@@ -356,14 +357,14 @@ func sayDroppedOnce(dropped []string) {
|
||||
"declares, and they are left out of it: %s (issue 1945)\n", len(dropped), key)
|
||||
}
|
||||
|
||||
func scopeName(c delivery.Composed) string {
|
||||
func scopeName(c compositions.Composed) string {
|
||||
if c.Module == "" {
|
||||
return agent.NodeModule
|
||||
}
|
||||
return c.Module
|
||||
}
|
||||
|
||||
func resourcesOf(cs []delivery.Composed) []declaration.Resource {
|
||||
func resourcesOf(cs []compositions.Composed) []declaration.Resource {
|
||||
var out []declaration.Resource
|
||||
for _, c := range cs {
|
||||
for _, raw := range c.Resources {
|
||||
@@ -445,7 +446,7 @@ func nodeAgent(ctx context.Context, opts options, signer ed25519.PublicKey, trus
|
||||
}
|
||||
began := time.Now()
|
||||
node := session.Membership.Node
|
||||
composedBucket, err := delivery.ComposedBucketFor(node)
|
||||
composedBucket, err := compositions.BucketFor(node)
|
||||
if err != nil {
|
||||
cancel()
|
||||
say("the node agent cannot name this node's composition bucket: " + err.Error())
|
||||
@@ -469,7 +470,7 @@ func nodeAgent(ctx context.Context, opts options, signer ed25519.PublicKey, trus
|
||||
// composes its own (ADR 0555 §3): without it a module that asks for a certificate would be stood in for.
|
||||
if byModule, _ := kept(); byModule != nil {
|
||||
for module, resources := range byModule {
|
||||
if err := keepAuthority(opts.state, delivery.Composed{Module: module, Resources: resources}); err != nil {
|
||||
if err := keepAuthority(opts.state, compositions.Composed{Module: module, Resources: resources}); err != nil {
|
||||
say("the mesh's certificate the kept declaration carries could not be kept for this machine's record: " + err.Error())
|
||||
}
|
||||
}
|
||||
@@ -478,7 +479,7 @@ func nodeAgent(ctx context.Context, opts options, signer ed25519.PublicKey, trus
|
||||
// bus module this node runs declares, as its agent holds what runs (its own composition, or the declaration kept
|
||||
// before the switch): never read from the node's composition bucket, which the controller wrote. On every node;
|
||||
// a node that runs no list writes nothing. Looked at again whenever what runs changed (RunsChanged, Settled).
|
||||
users := userListFollower(opts, session, func() []delivery.Composed {
|
||||
users := userListFollower(opts, session, func() []compositions.Composed {
|
||||
if now := current.get(); now != nil {
|
||||
return now.Everything()
|
||||
}
|
||||
@@ -545,7 +546,7 @@ func nodeAgent(ctx context.Context, opts options, signer ed25519.PublicKey, trus
|
||||
Say: say,
|
||||
// A failure of the bus not answering for a moment is tried again: 2 s, 5 s, 15 s, 60 s, then every 5 min.
|
||||
Backoff: link.Backoff,
|
||||
Apply: func(ctx context.Context, c delivery.Composed, others []delivery.Composed) agent.Result {
|
||||
Apply: func(ctx context.Context, c compositions.Composed, others []compositions.Composed) agent.Result {
|
||||
actx, done := applyContext(session.WithLink(ctx))
|
||||
// While the grants part's list is followed, the controller's list in the bus module's composition is not
|
||||
// written: the apply keeps the list on the machine (ADR 0547 clause 8, never both).
|
||||
@@ -571,7 +572,7 @@ func nodeAgent(ctx context.Context, opts options, signer ed25519.PublicKey, trus
|
||||
sayFacts(ctx, ag)
|
||||
return res
|
||||
},
|
||||
Remove: func(ctx context.Context, module string, others []delivery.Composed) agent.Result {
|
||||
Remove: func(ctx context.Context, module string, others []compositions.Composed) agent.Result {
|
||||
actx, done := applyContext(session.WithLink(ctx))
|
||||
res := removeComposition(actx, opts, module, others, ag.Placed(module), sched, say)
|
||||
done()
|
||||
@@ -595,7 +596,7 @@ func nodeAgent(ctx context.Context, opts options, signer ed25519.PublicKey, trus
|
||||
}
|
||||
hooks := composerHooks{
|
||||
seatList: seats,
|
||||
running: func() []delivery.Composed {
|
||||
running: func() []compositions.Composed {
|
||||
if now := own.get(); now != nil {
|
||||
return runsAsWritten(now.Everything(), opts.state)
|
||||
}
|
||||
@@ -639,8 +640,8 @@ func nodeAgent(ctx context.Context, opts options, signer ed25519.PublicKey, trus
|
||||
shadowHooks.refused, shadowHooks.kept, shadowHooks.certify, shadowHooks.seatList = nil, nil, nil, nil
|
||||
shadowEnv := composerEnv(session, node, say, shadowHooks)
|
||||
shadow := selfcompose.NewShadow(shadowEnv,
|
||||
func(ctx context.Context, answers map[string]delivery.CandidateAnswer) {
|
||||
session.NoteCandidates(ctx, delivery.WithValuesDiffer(answers))
|
||||
func(ctx context.Context, answers map[string]nodeengine.CandidateAnswer) {
|
||||
session.NoteCandidates(ctx, compositions.WithValuesDiffer(answers))
|
||||
})
|
||||
go shadow.Keep(run, link.Backoff) // this run's alone: a switch begins a run, and its shadow, again
|
||||
shadowNow.Store(shadow) // what the verb compose-candidate composes a check's candidate with
|
||||
@@ -712,7 +713,7 @@ func (c *agentNow) get() *agent.Agent {
|
||||
//
|
||||
// removed is the modules whose want says removed: in the node's own apply, a resource of any other module goes only when
|
||||
// its module is among them (issue 774).
|
||||
func applyComposition(ctx context.Context, opts options, c delivery.Composed, others []delivery.Composed, placed, removed map[string]bool,
|
||||
func applyComposition(ctx context.Context, opts options, c compositions.Composed, others []compositions.Composed, placed, removed map[string]bool,
|
||||
sched *apply.Scheduler, say link.Announce) agent.Result {
|
||||
name := scopeName(c)
|
||||
if c.Module == "" {
|
||||
@@ -760,7 +761,7 @@ func applyComposition(ctx context.Context, opts options, c delivery.Composed, ot
|
||||
}
|
||||
|
||||
// removeComposition removes one module's own resources and nothing of another's.
|
||||
func removeComposition(ctx context.Context, opts options, module string, others []delivery.Composed, placed map[string]bool,
|
||||
func removeComposition(ctx context.Context, opts options, module string, others []compositions.Composed, placed map[string]bool,
|
||||
sched *apply.Scheduler, say link.Announce) agent.Result {
|
||||
scope := apply.Scope{Module: module, Others: resourcesOf(others), Placed: placed}
|
||||
got := applyModuleHeld(ctx, opts, &declaration.Declaration{Version: declaration.Version}, &scope, sched, say)
|
||||
@@ -833,7 +834,7 @@ func keptDeclaration(statePath string, signer ed25519.PublicKey) (*declaration.D
|
||||
// (adoption, identities, groups, libraries) from the node's composition once the agent holds it and from the kept
|
||||
// declaration until then. What the node judges its health by, and what the filling, the take act and the judgement of
|
||||
// directories read.
|
||||
func whatRuns(everything []delivery.Composed, holdsNode bool, kept func() (*declaration.Declaration, error)) (*declaration.Declaration, error) {
|
||||
func whatRuns(everything []compositions.Composed, holdsNode bool, kept func() (*declaration.Declaration, error)) (*declaration.Declaration, error) {
|
||||
d, err := declarationOf(everything...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -919,8 +920,8 @@ func refuseEmptyWrites(d *declaration.Declaration) error {
|
||||
// recordedIn says the engine's applied state holds a record of every resource of a composition: what the agent believes
|
||||
// of a have or the kept declaration only when it does, so a resource removed since is made again (issue 1945). A state
|
||||
// that cannot be read says yes: the agent then believes what it was told, as before.
|
||||
func recordedIn(statePath string, say func(string)) func(ctx context.Context, c delivery.Composed) bool {
|
||||
return func(_ context.Context, c delivery.Composed) bool {
|
||||
func recordedIn(statePath string, say func(string)) func(ctx context.Context, c compositions.Composed) bool {
|
||||
return func(_ context.Context, c compositions.Composed) bool {
|
||||
known, err := store.Load(statePath)
|
||||
if err != nil {
|
||||
if say != nil {
|
||||
@@ -982,7 +983,7 @@ func applyContext(ctx context.Context) (context.Context, context.CancelFunc) {
|
||||
|
||||
// composerHooks are what the composer reads of the agent and the machine (selfcompose.Env Running, Unseal, Refused).
|
||||
type composerHooks struct {
|
||||
running func() []delivery.Composed
|
||||
running func() []compositions.Composed
|
||||
unseal func(string) ([]byte, error)
|
||||
refused func(why string, held map[string]string)
|
||||
kept func(kept map[string][]string)
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"crypto/ed25519"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
nodeengine "git.novox.be/novox/mesh-contracts/go/seats/nodeengine"
|
||||
engcomp "github.com/novox/mesh-host/internal/compositions"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -60,7 +62,9 @@ type itBed struct {
|
||||
mu sync.Mutex
|
||||
// seatSilent makes the stand-in of the seat mesh-catalog refuse the seat list (it is served from the start otherwise).
|
||||
seatSilent atomic.Bool
|
||||
findings nats.KeyValue
|
||||
// seatAsked counts the node's asks of the stand-in seat, answered or refused: the node reached the bus and asked.
|
||||
seatAsked atomic.Int32
|
||||
findings nats.KeyValue
|
||||
}
|
||||
|
||||
// aServer is an embedded bus with JetStream and two users: the test's, unrestricted, and the node's, granted exactly what
|
||||
@@ -126,12 +130,12 @@ func newItBedAs(t *testing.T, perms nodegrant.Permissions) *itBed {
|
||||
if b.nodeJS, err = b.nodeConn.JetStream(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
composedBucket, _ := delivery.ComposedBucketFor(itNode)
|
||||
composedBucket, _ := engcomp.BucketFor(itNode)
|
||||
for _, c := range []struct {
|
||||
name string
|
||||
into *nats.KeyValue
|
||||
history uint8
|
||||
}{{delivery.AssignerWantsBucket, &b.wants, 8}, {composedBucket, &b.composed, delivery.ComposedHistory},
|
||||
}{{delivery.AssignerWantsBucket, &b.wants, 8}, {composedBucket, &b.composed, engcomp.History},
|
||||
{delivery.HaveBucket, &b.haves, delivery.HaveHistory}} {
|
||||
kv, err := b.js.CreateKeyValue(&nats.KeyValueConfig{Bucket: c.name, History: c.history})
|
||||
if err != nil {
|
||||
@@ -253,7 +257,7 @@ func TestNatsANodeThePlacementNamesComposesAndAppliesItsOwn(t *testing.T) {
|
||||
within(t, 5*time.Second, "the node did not compose and apply its own module once placed", func() bool { return b.file("web") == "greeting=bonjour\n" })
|
||||
// ADR 0565 §6 (1953): the node's engine writes its own key of the state packages, under the grant the grants part
|
||||
// composes for it, with each package its compositions declare and the module declaring it; once, while nothing changes.
|
||||
wantPkgs := `{"node":"` + itNode + `","packages":[{"package":"web-tools","modules":["web"],"absent":false}]}`
|
||||
wantPkgs := `{"node":"` + itNode + `","packages":[{"absent":false,"modules":["web"],"package":"web-tools"}]}`
|
||||
within(t, 5*time.Second, "the node did not write its key of the state packages", func() bool {
|
||||
e, err := st.packages.Get(itNode)
|
||||
return err == nil && string(e.Value()) == wantPkgs
|
||||
@@ -351,7 +355,7 @@ func h8back(t *testing.T, b *itBed, placement, resolved, candidates nats.KeyValu
|
||||
}
|
||||
within(t, 10*time.Second, "the node did not refuse a candidate taking its placement away", func() bool {
|
||||
a := node().Candidates["revert-x"]
|
||||
return a.Proposal == "revert-x" && a.Returns == "" && a.Verdict == delivery.CandidateRefused &&
|
||||
return a.Proposal == "revert-x" && a.Returns == "" && a.Verdict == nodeengine.CandidateAnswerVerdictRefused &&
|
||||
strings.Contains(a.Refused, "composes nothing")
|
||||
})
|
||||
ran := b.have("web").Composed
|
||||
@@ -504,17 +508,17 @@ func h8rest(t *testing.T, b *itBed, resolved, candidates nats.KeyValue, hkey str
|
||||
// ADR 0593 §6: the node publishes what it composed in its composition state, under its own grant: web at the build
|
||||
// its want names, with the digest its have names (the plan left out of it), and the node's own.
|
||||
first := b.have("web").Composed
|
||||
webKey, _ := delivery.ComposedKey(itNode, "web", fp)
|
||||
webKey, _ := engcomp.Key(itNode, "web", fp)
|
||||
within(t, 5*time.Second, "the node did not publish web's composition in its composition state", func() bool {
|
||||
e, err := b.composed.Get(webKey)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
c, err := delivery.DecodeComposed(e.Value())
|
||||
c, err := engcomp.Decode(e.Value())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
d, err := delivery.ComposedDigestOf(c)
|
||||
d, err := engcomp.DigestOf(c)
|
||||
return err == nil && d == first
|
||||
})
|
||||
if _, err := resolved.Put(itNode+".web.greeting", []byte(`{"value":"salut","source":"module"}`)); err != nil {
|
||||
@@ -541,17 +545,18 @@ func h8rest(t *testing.T, b *itBed, resolved, candidates nats.KeyValue, hkey str
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var answer delivery.CandidateAnswer
|
||||
var answer nodeengine.CandidateAnswer
|
||||
within(t, 10*time.Second, "the node did not answer its candidate", func() bool {
|
||||
e, err := b.haves.Get(hkey)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
n, err := delivery.DecodeHaveNode(e.Value())
|
||||
answer = n.Candidates["feat-x"]
|
||||
wire, _ := json.Marshal(n.Candidates["feat-x"])
|
||||
_ = json.Unmarshal(wire, &answer)
|
||||
return err == nil && answer.Proposal == "feat-x" && n.Record != nil
|
||||
})
|
||||
if answer.Verdict != delivery.CandidateSame || answer.Revision != crev || len(answer.Same) != 1 || answer.Same[0] != "web" {
|
||||
if answer.Verdict != nodeengine.CandidateAnswerVerdictSame || answer.Revision != int64(crev) || len(answer.Same) != 1 || answer.Same[0] != "web" {
|
||||
t.Errorf("the answer to a candidate equal to the placement: %+v", answer)
|
||||
}
|
||||
if h := b.have("web"); h.Composed != second || b.file("web") != "greeting=salut\n" {
|
||||
@@ -629,7 +634,7 @@ func h8candidate(t *testing.T, b *itBed, fp, running string) {
|
||||
|
||||
func mustComposedKey(t *testing.T, module, fp string) string {
|
||||
t.Helper()
|
||||
key, err := delivery.ComposedKey(itNode, module, fp)
|
||||
key, err := engcomp.Key(itNode, module, fp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -904,6 +909,7 @@ func (b *itBed) standInSeat() {
|
||||
Seat string `json:"seat"`
|
||||
}
|
||||
_ = json.Unmarshal(m.Data, &q)
|
||||
b.seatAsked.Add(1)
|
||||
var result any
|
||||
switch {
|
||||
case b.seatSilent.Load():
|
||||
@@ -979,6 +985,17 @@ func TestNatsANodeThatCannotReadTheSeatListActsOnNothingNew(t *testing.T) {
|
||||
defer clock.mu.Unlock()
|
||||
return len(clock.fire) > 0
|
||||
})
|
||||
// A fresh node, nothing applied, reaches the bus and asks the seat while its own composition is held: the read needs
|
||||
// the bus the node composes from and the grant it has from its start, nothing of what the hold keeps back.
|
||||
if b.seatAsked.Load() == 0 {
|
||||
t.Fatal("the fresh node did not ask the seat for its list while its own composition was held")
|
||||
}
|
||||
within(t, 5*time.Second, "the fresh node did not say it holds web and its own composition for the unread list", func() bool {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
said := strings.Join(b.said, "\n")
|
||||
return strings.Contains(said, selfcompose.WaitsForTheSeatListOwn) && strings.Contains(said, selfcompose.WaitsForTheSeatList)
|
||||
})
|
||||
if b.file("web") != "" {
|
||||
t.Fatalf("the node applied web before it read the seat list: %q", b.file("web"))
|
||||
}
|
||||
|
||||
@@ -3,11 +3,10 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
engcomp "github.com/novox/mesh-host/internal/compositions"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
|
||||
"github.com/novox/mesh-host/internal/liveness"
|
||||
"github.com/novox/mesh-host/internal/store"
|
||||
)
|
||||
@@ -21,7 +20,7 @@ func TestACompositionIsRecordedOnlyWhenEveryResourceIs(t *testing.T) {
|
||||
if err := store.Save(path, s); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
c := delivery.Composed{Node: "ace", Module: "ca-trust", Resources: []json.RawMessage{
|
||||
c := engcomp.Composed{Node: "ace", Module: "ca-trust", Resources: []json.RawMessage{
|
||||
json.RawMessage(`{"id":"ca-trust.trust","type":"file","module":"ca-trust","path":"/etc/ca","mode":"0644","content":"x"}`)}}
|
||||
recorded := recordedIn(path, func(string) {})
|
||||
if !recorded(context.Background(), c) {
|
||||
@@ -87,7 +86,7 @@ func TestAFilledFileDoesNotMakeItsCompositionUnrecorded(t *testing.T) {
|
||||
if err := store.Save(path, s); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
c := delivery.Composed{Node: "ace", Module: "ca-trust", Filled: []string{"/etc/ca-filled"}, Resources: []json.RawMessage{
|
||||
c := engcomp.Composed{Node: "ace", Module: "ca-trust", Filled: []string{"/etc/ca-filled"}, Resources: []json.RawMessage{
|
||||
json.RawMessage(`{"id":"ca-trust.trust","type":"file","module":"ca-trust","path":"/etc/ca","mode":"0644","content":"x"}`),
|
||||
json.RawMessage(`{"id":"ca-trust.filled","type":"file","module":"ca-trust","path":"/etc/ca-filled","mode":"0644","owner-at-runtime":"owned"}`)}}
|
||||
if !recordedIn(path, func(string) {})(context.Background(), c) {
|
||||
|
||||
@@ -4,10 +4,10 @@ import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
engcomp "github.com/novox/mesh-host/internal/compositions"
|
||||
"os"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/declaration"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
|
||||
"github.com/novox/mesh-host/internal/store"
|
||||
)
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
// names what is on disk: **only when the node's record says it wrote exactly that content there** (store Applied.Wrote,
|
||||
// Target), never a file someone else changed. A file that cannot be read, or that the record does not vouch for, is
|
||||
// left as the composition names it.
|
||||
func runsAsWritten(running []delivery.Composed, statePath string) []delivery.Composed {
|
||||
func runsAsWritten(running []engcomp.Composed, statePath string) []engcomp.Composed {
|
||||
if len(running) == 0 || statePath == "" {
|
||||
return running
|
||||
}
|
||||
@@ -28,7 +28,7 @@ func runsAsWritten(running []delivery.Composed, statePath string) []delivery.Com
|
||||
if err != nil {
|
||||
return running
|
||||
}
|
||||
out := make([]delivery.Composed, len(running))
|
||||
out := make([]engcomp.Composed, len(running))
|
||||
for i, c := range running {
|
||||
out[i] = c
|
||||
var changed []json.RawMessage
|
||||
|
||||
@@ -110,7 +110,7 @@ func (w findingsOfTheNode) encode(f views.Finding) (string, []byte, error) {
|
||||
key := conditions.Key(conditions.ScopeMachine, f.ID, f.Kind)
|
||||
v := condseat.FindingsByKeyValue{V: 1, Key: key, Kind: f.Kind, Observer: f.Observer, Severity: conditions.Warning,
|
||||
Subject: condseat.Subject{Scope: conditions.ScopeMachine, ID: f.ID, Machine: f.ID}, Summary: f.Summary,
|
||||
Headline: f.Headline, Explanation: f.Explanation, Source: "seat-list-unread", At: f.At.UTC().Format(time.RFC3339)}
|
||||
Headline: f.Headline, Explanation: f.Explanation, Source: "seat-list-unread", At: f.At.UTC()}
|
||||
if err := conditions.CheckFinding(v); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
engcomp "github.com/novox/mesh-host/internal/compositions"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@@ -25,7 +26,7 @@ import (
|
||||
|
||||
// userListFollower is the follower of the bus's user list for one link: the node's composition bucket, the keys the
|
||||
// agent trusts, the write alone, and the have.
|
||||
func userListFollower(opts options, session *link.Session, running func() []delivery.Composed,
|
||||
func userListFollower(opts options, session *link.Session, running func() []engcomp.Composed,
|
||||
say link.Announce, wrote func()) *userlist.Follower {
|
||||
return userlist.New(userlist.Env{
|
||||
Node: session.Membership.Node,
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"crypto/ed25519"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
engcomp "github.com/novox/mesh-host/internal/compositions"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -31,7 +32,7 @@ func aFileOf(module, id string) json.RawMessage {
|
||||
func TestWhatRunsIsTheAgentsCompositionsAndTheKeptRest(t *testing.T) {
|
||||
kept := &declaration.Declaration{Identities: []declaration.Identity{{Module: "db", Name: "mesh-db"}},
|
||||
Libraries: []declaration.Library{{Library: "media"}}}
|
||||
everything := []delivery.Composed{
|
||||
everything := []engcomp.Composed{
|
||||
{Node: "n1", Module: "web", Resources: []json.RawMessage{aFileOf("web", "a")}},
|
||||
{Node: "n1", Module: "db", Resources: []json.RawMessage{aFileOf("db", "a")}}, // still the kept declaration's
|
||||
}
|
||||
@@ -45,7 +46,7 @@ func TestWhatRunsIsTheAgentsCompositionsAndTheKeptRest(t *testing.T) {
|
||||
}
|
||||
|
||||
// Once the agent holds the node's composition, its parts are the node's.
|
||||
node := delivery.Composed{Node: "n1", Own: delivery.NodeOwn{Identities: []declaration.Identity{{Module: "web", Name: "mesh-web"}}}}
|
||||
node := engcomp.Composed{Node: "n1", Own: engcomp.NodeOwn{Identities: []declaration.Identity{{Module: "web", Name: "mesh-web"}}}}
|
||||
d, err = whatRuns(append(everything, node), true, func() (*declaration.Declaration, error) { return nil, errors.New("not read") })
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -88,7 +89,7 @@ func TestDeclaredNowReadsWhatRunsWhenTheAgentIsRunning(t *testing.T) {
|
||||
for _, name := range row.runs {
|
||||
raws = append(raws, process(name))
|
||||
}
|
||||
runs, err := whatRuns([]delivery.Composed{{Node: "n1", Module: "restic", Resources: raws}}, false,
|
||||
runs, err := whatRuns([]engcomp.Composed{{Node: "n1", Module: "restic", Resources: raws}}, false,
|
||||
func() (*declaration.Declaration, error) { return nil, errors.New("none kept") })
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -161,9 +162,9 @@ func TestDeclaredNowReadsWhatRunsWhenTheAgentIsRunning(t *testing.T) {
|
||||
// the module's: the declaration joined of both names each file once, so the filling writes it; the node's own resources
|
||||
// no module declares stay. (Live from 2026-10-25 12:43: "2 resources declare X" on every node.)
|
||||
func TestTheNodesOwnCopyOfAModulesResourceIsLeftOut(t *testing.T) {
|
||||
own := delivery.Composed{Node: "n1", Resources: []json.RawMessage{aFileOf("zsh", "rc"), aFileOf("mesh-wireguard", "conf")},
|
||||
own := engcomp.Composed{Node: "n1", Resources: []json.RawMessage{aFileOf("zsh", "rc"), aFileOf("mesh-wireguard", "conf")},
|
||||
Filled: []string{"/tmp/r6/zsh/rc"}}
|
||||
zsh := delivery.Composed{Node: "n1", Module: "zsh", FP: "src2:aa", Resources: []json.RawMessage{aFileOf("zsh", "rc")},
|
||||
zsh := engcomp.Composed{Node: "n1", Module: "zsh", FP: "src2:aa", Resources: []json.RawMessage{aFileOf("zsh", "rc")},
|
||||
Filled: []string{"/tmp/r6/zsh/rc"}}
|
||||
d, err := declarationOf(own, zsh)
|
||||
if err != nil {
|
||||
@@ -181,7 +182,7 @@ func TestTheNodesOwnCopyOfAModulesResourceIsLeftOut(t *testing.T) {
|
||||
}
|
||||
|
||||
// The same at the node's own apply: what the others declare is theirs.
|
||||
trimmed, dropped := ownWithout(own, []delivery.Composed{zsh})
|
||||
trimmed, dropped := ownWithout(own, []engcomp.Composed{zsh})
|
||||
if len(trimmed.Resources) != 1 || len(dropped) != 1 || dropped[0] != "zsh.rc (zsh)" {
|
||||
t.Errorf("the node's own without zsh's: %d resources, dropped %v", len(trimmed.Resources), dropped)
|
||||
}
|
||||
@@ -211,9 +212,9 @@ func TestTheKeptDeclarationsFilledPathsGoToTheirModules(t *testing.T) {
|
||||
strings.Join(filled[agent.NodeModule], ",") != "/etc/motd" || len(filled["zsh"]) != 0 {
|
||||
t.Fatalf("filled by module: %v", filled)
|
||||
}
|
||||
cc := delivery.Composed{Node: "n1", Module: "claude-code", Resources: by["claude-code"], Filled: filled["claude-code"]}
|
||||
node := delivery.Composed{Node: "n1"}
|
||||
d, err := whatRuns([]delivery.Composed{cc, node}, true, func() (*declaration.Declaration, error) { return nil, errors.New("not read") })
|
||||
cc := engcomp.Composed{Node: "n1", Module: "claude-code", Resources: by["claude-code"], Filled: filled["claude-code"]}
|
||||
node := engcomp.Composed{Node: "n1"}
|
||||
d, err := whatRuns([]engcomp.Composed{cc, node}, true, func() (*declaration.Declaration, error) { return nil, errors.New("not read") })
|
||||
if err != nil || !d.Fills("/etc/cc.json") {
|
||||
t.Fatalf("what runs does not name claude-code's file filled: %v (%v)", d, err)
|
||||
}
|
||||
@@ -226,9 +227,9 @@ func TestTheKeptDeclarationsFilledPathsGoToTheirModules(t *testing.T) {
|
||||
// unit or a container of the same name) is not the module's, and is neither dropped nor left to be removed.
|
||||
func TestASharedTargetWithAnotherIDIsNotDropped(t *testing.T) {
|
||||
ownFile, _ := json.Marshal(map[string]any{"id": "host.zshrc", "type": "file", "path": "/tmp/r6/zsh/rc", "content": "x"})
|
||||
own := delivery.Composed{Node: "n1", Resources: []json.RawMessage{ownFile}}
|
||||
zsh := delivery.Composed{Node: "n1", Module: "zsh", FP: "src2:aa", Resources: []json.RawMessage{aFileOf("zsh", "rc")}}
|
||||
trimmed, dropped := ownWithout(own, []delivery.Composed{zsh})
|
||||
own := engcomp.Composed{Node: "n1", Resources: []json.RawMessage{ownFile}}
|
||||
zsh := engcomp.Composed{Node: "n1", Module: "zsh", FP: "src2:aa", Resources: []json.RawMessage{aFileOf("zsh", "rc")}}
|
||||
trimmed, dropped := ownWithout(own, []engcomp.Composed{zsh})
|
||||
if len(dropped) != 0 || len(trimmed.Resources) != 1 {
|
||||
t.Errorf("a resource sharing only its target was dropped: %v", dropped)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user