A modules value with no verdict yet holds only its module's users, and one module's value that does not read is a gap of that module, never of the whole list (ADR 0614 §8, issue 774)
The indexer writes a changed manifest's value before it judges it; the part now reads the indexer's verdicts. Claude-Session: https://claude.ai/code/session_01JG73KG7AfSdh6FP8VrBqCn
This commit is contained in:
@@ -93,6 +93,8 @@ func TestTheGrantsPartWhole(t *testing.T) {
|
||||
put(grants.ReadManifests, digest, delivery.Manifest{V: delivery.Version, Module: module, JSON: raw})
|
||||
put(grants.ReadModules, module, delivery.Module{V: delivery.Version, FP2: "src2:" + strings.Repeat("ab", 32), Manifest: digest,
|
||||
Repository: "novox/x", Commit: "c"})
|
||||
verdict, _ := delivery.EncodeManifestVerdict(delivery.ManifestVerdict{Module: module, State: delivery.VerdictAccepted, Judge: "j", At: time.Now()})
|
||||
put(grants.ReadVerdicts, digest, json.RawMessage(verdict))
|
||||
}
|
||||
manifest("nats", `{"module":"nats","version":"1","claims":[{"name":"mesh-broker"}],"own-secrets":{"broker":"/var/lib/nats/broker"}}`)
|
||||
manifest("mod-c", `{"module":"mod-c","version":"1","own-secrets":{"broker":"/var/lib/c/broker"},"consumes":["nats.done"]}`)
|
||||
|
||||
@@ -154,6 +154,12 @@ func (b *fakeBus) put(state, key string, v any) {
|
||||
b.states[state] = map[string][]byte{}
|
||||
}
|
||||
b.states[state][key] = raw
|
||||
// A modules value that carries no verdict is accepted by the indexer's verdict on its digest, as the indexer writes it;
|
||||
// a row that wants the window before the judge deletes that key.
|
||||
if m, ok := v.(delivery.Module); ok && state == grants.ReadModules && m.Verdict == "" && m.Manifest != "" {
|
||||
verdict, _ := delivery.EncodeManifestVerdict(delivery.ManifestVerdict{Module: key, State: delivery.VerdictAccepted, Judge: "j", At: time.Now()})
|
||||
b.put(grants.ReadVerdicts, m.Manifest, json.RawMessage(verdict))
|
||||
}
|
||||
}
|
||||
|
||||
func (b *fakeBus) Keys(state string) ([]string, error) {
|
||||
@@ -2841,6 +2847,20 @@ func TestGrants(t *testing.T) {
|
||||
if last := said[len(said)-1]; last.State != asks.StateRaised || !strings.Contains(last.Summary, "the manifest of mod-r is unreadable") {
|
||||
t.Errorf("the unreadable manifest was not said: %+v", last)
|
||||
}
|
||||
// The window before the judge: the indexer wrote a changed manifest's value and no verdict on its digest yet. Only that
|
||||
// module's users stay (named unreadable); the others move.
|
||||
b.manifestOf("mod-r", ctl)
|
||||
delete(b.states[grants.ReadVerdicts], delivery.ManifestDigest([]byte(ctl)))
|
||||
b.put(grants.ReadAccounts, "a.mod-x", map[string]any{"user": "a.mod-x", "hash": "$2a$10$gen11", "generation": 11, "fingerprint": "sha256:11"})
|
||||
wasList, _, _ = p.Sealed()
|
||||
res = p.Compose()
|
||||
nowList, _, _ = p.Sealed()
|
||||
if nowList.Digest == wasList.Digest || strings.Contains(fmt.Sprint(res.Sealed), "kept while a read") {
|
||||
t.Errorf("a manifest with no verdict yet held the whole list: %+v", res.Sealed)
|
||||
}
|
||||
if last := said[len(said)-1]; last.State != asks.StateRaised || !strings.Contains(last.Summary, "no verdict on this manifest yet") {
|
||||
t.Errorf("a manifest with no verdict yet was not said: %+v", last)
|
||||
}
|
||||
// Accepted again: the users compose from the manifest and the condition clears.
|
||||
b.manifestOf("mod-r", ctl)
|
||||
if p.Compose(); said[len(said)-1].State != asks.StateCleared {
|
||||
|
||||
@@ -21,7 +21,7 @@ type Watcher interface {
|
||||
//
|
||||
// And the seat mesh-enrolment's keys (ADR 0587 §4, §5): a key at live composes enrol.<node>, and its end, by the holder or by
|
||||
// the bus's expiry, is a change of the key that takes the user out; no event is needed beside the watch.
|
||||
var Followed = []string{ReadModules, ReadResolved, ReadHave, ReadWants, ReadAccounts, ReadPlacement, ReadEnrolments,
|
||||
var Followed = []string{ReadModules, ReadVerdicts, ReadResolved, ReadHave, ReadWants, ReadAccounts, ReadPlacement, ReadEnrolments,
|
||||
ReadNodeLogins}
|
||||
|
||||
// Follower composes again on every change it hears, never on a timer. It never stops for a refusal: a watch it cannot
|
||||
|
||||
@@ -25,10 +25,13 @@ import (
|
||||
const (
|
||||
ReadModules = "mesh-delivery-indexer.modules"
|
||||
ReadManifests = "mesh-delivery-indexer.manifests"
|
||||
ReadHave = "node-engine.have"
|
||||
ReadResolved = "mesh-config.resolved"
|
||||
ReadWants = "mesh-delivery-assigner.wants"
|
||||
ReadBuilds = "mesh-delivery-builder.builds"
|
||||
// ReadVerdicts is the indexer's verdicts, one key per manifest digest: a manifest it has not judged yet has none, and is
|
||||
// held like a refused one (ADR 0614 §8).
|
||||
ReadVerdicts = "mesh-delivery-indexer.verdicts"
|
||||
ReadHave = "node-engine.have"
|
||||
ReadResolved = "mesh-config.resolved"
|
||||
ReadWants = "mesh-delivery-assigner.wants"
|
||||
ReadBuilds = "mesh-delivery-builder.builds"
|
||||
// ReadAccounts is the vault's bcrypt hashes of the bus accounts it holds, by bus user (ADR 0547 clause 9).
|
||||
ReadAccounts = "mesh-vault.bus-accounts"
|
||||
// ReadPlacement is the seat mesh-placement's placement (ADR 0567 §1, §9): each node's modules and the seats
|
||||
@@ -44,7 +47,7 @@ const (
|
||||
|
||||
// Reads is every state the part reads, in the manifest's order. Not the controller's assignments: the seat's placement
|
||||
// alone places modules (novox/hq ADR 0549 §5, ADR 0571 §10; issue 774).
|
||||
var Reads = []string{ReadModules, ReadManifests, ReadResolved, ReadHave, ReadWants, ReadBuilds,
|
||||
var Reads = []string{ReadModules, ReadManifests, ReadVerdicts, ReadResolved, ReadHave, ReadWants, ReadBuilds,
|
||||
ReadAccounts, ReadPlacement, ReadEnrolments, ReadNodeLogins}
|
||||
|
||||
// The bus module's tools the part calls, as its manifest's invokes name them: read-only, each.
|
||||
@@ -336,7 +339,10 @@ func ReadInputs(r Reader, cache *Manifests) Inputs {
|
||||
for _, module := range indexed {
|
||||
raw, found, err := r.Get(ReadModules, module)
|
||||
if err != nil {
|
||||
in.Gaps = append(in.Gaps, Gap{Input: ReadModules + " " + module, Why: err.Error(), Failed: true})
|
||||
// One module's value that does not read holds that module alone, as a refused manifest does (ADR 0614 §8).
|
||||
g := Gap{Input: "manifest of " + module, Why: "its value in " + ReadModules + " does not read: " + err.Error()}
|
||||
in.Refused = append(in.Refused, g)
|
||||
in.Gaps = append(in.Gaps, g)
|
||||
continue
|
||||
}
|
||||
if !found {
|
||||
@@ -360,6 +366,10 @@ func ReadInputs(r Reader, cache *Manifests) Inputs {
|
||||
// The indexer's refusal, or its wait for a judge that knows a field, carried in its value (ADR 0578 §3): held and named as a refused manifest.
|
||||
if verdict := v.VerdictOf(module); verdict != nil && verdict.Held() {
|
||||
err = errors.New(verdict.Why())
|
||||
} else if verdict == nil {
|
||||
// The indexer writes a changed manifest's value before it judges it, and a new digest has no verdict: until
|
||||
// the verdict is written the manifest is held (ADR 0614 §8).
|
||||
err = noVerdictYet(r, v.Manifest)
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
@@ -651,3 +661,27 @@ func (in Inputs) declared() map[string]catalogue.Manifest {
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// noVerdictYet is nil when the indexer's verdicts state holds an accepted verdict on the manifest, and the reason it is held
|
||||
// when it holds none yet, cannot be read, or refuses it.
|
||||
func noVerdictYet(r Reader, digest string) error {
|
||||
key, err := delivery.VerdictsKey(digest)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
raw, found, err := r.Get(ReadVerdicts, key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("its verdict cannot be read: %w", err)
|
||||
}
|
||||
if !found {
|
||||
return errors.New("the indexer has written no verdict on this manifest yet")
|
||||
}
|
||||
v, err := delivery.DecodeManifestVerdict(raw)
|
||||
if err != nil {
|
||||
return fmt.Errorf("its verdict does not read: %w", err)
|
||||
}
|
||||
if v.Held() {
|
||||
return errors.New(v.Why())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"mesh-delivery-builder.builds",
|
||||
"mesh-delivery-indexer.manifests",
|
||||
"mesh-delivery-indexer.modules",
|
||||
"mesh-delivery-indexer.verdicts",
|
||||
"mesh-delivery-assigner.placement",
|
||||
"node-engine.have",
|
||||
"mesh-vault.bus-accounts",
|
||||
|
||||
Reference in New Issue
Block a user