Judge every read for its reader where the bus's users are composed (issue 774)
Since 13:25 the vault, the assigner, the watchman, the grants part and the artifact store's tools were refused mesh-controller.lease, and the vault mesh-grants.accounts, though the composer names them those states' readers (ADR 0547 clause 9): the controller splits reads with its own copy of the reader-less ReadsAnnounced, which knows no named readers, and Users only moved reads from granted to refused, never back. Quick fix: the controller calls ReadsAnnouncedBy with the module. Thorough fix, chosen because the kind was met on four owners and the judgement had two copies: Users judges every read again for its reader against the announcements in Records (withAnnouncedReads), whatever split the caller made, and the reader-less ReadsAnnounced is removed so no caller can judge without the reader. A rule over manifests was not added: ADR 0495 clauses 1 and 5 keep readability out of the manifest and replaced the merge gate's reads check by each module's announcement test. gitea.bound and disk-load.readings stay refused: their owners announce them not readable, which their own code fixes.
This commit is contained in:
@@ -42,16 +42,12 @@ func ControllerAnnouncement(node string) announce.States {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadsAnnounced splits a reader's `reads` (`<owner>.<name>`) by what each owner announced: granted when the owner
|
// ReadsAnnouncedBy splits one reader module's `reads` (`<owner>.<name>`) by what each owner announced: granted when the
|
||||||
// announced the state readable, or announced nothing yet (the transition); refused when the owner announced and did
|
// owner announced the state readable (by this reader, where it named its readers: announce.State.Readers), when the
|
||||||
// not announce that state readable.
|
// composer names the reader among the state's readers (NamedReaders, ADR 0547 clause 9), or when the owner announced
|
||||||
func ReadsAnnounced(reads []string, announced map[string]announce.States) (granted, refused []string) {
|
// nothing yet (the transition); refused otherwise, and a read named for other modules is refused. There is no judgement
|
||||||
return ReadsAnnouncedBy("", reads, announced)
|
// without the reader (issue 774): one made without it refused every named reader, and Users judges again for the
|
||||||
}
|
// reader whatever split it is given (withAnnouncedReads). An empty reader is judged by the readable mark alone.
|
||||||
|
|
||||||
// ReadsAnnouncedBy is ReadsAnnounced for one reader module, which also honours the readers an owner named for a state
|
|
||||||
// (announce.State.Readers) and the composer's named readers (NamedReaders, ADR 0547 clause 9): a read named for other
|
|
||||||
// modules is refused. An empty reader is judged by the readable mark alone.
|
|
||||||
func ReadsAnnouncedBy(reader string, reads []string, announced map[string]announce.States) (granted, refused []string) {
|
func ReadsAnnouncedBy(reader string, reads []string, announced map[string]announce.States) (granted, refused []string) {
|
||||||
for _, r := range reads {
|
for _, r := range reads {
|
||||||
owner, name, ok := strings.Cut(r, ".")
|
owner, name, ok := strings.Cut(r, ".")
|
||||||
|
|||||||
@@ -552,6 +552,27 @@ func TestTheCopiedComposerComposesAsTheControllers(t *testing.T) {
|
|||||||
t.Errorf("%s reads the lease: %v, want %v", reader, granted, want)
|
t.Errorf("%s reads the lease: %v, want %v", reader, granted, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Issue 774 (sweep item 2): Users judges every read for its reader again, whatever split the caller made: the
|
||||||
|
// controller's own reader-less copy refused the vault the lease and the grants part's accounts it is named for.
|
||||||
|
announced["mesh-grants"] = announce.States{Module: "mesh-grants", States: []announce.State{{Name: "accounts", Readers: []string{VaultSeat}}}}
|
||||||
|
announced["disk-load"] = announce.States{Module: "disk-load", States: []announce.State{{Name: "readings"}}}
|
||||||
|
judged, err := Users(Records{Nodes: []string{"a"}, Announced: announced, Assigned: map[string][]Declared{"a": {
|
||||||
|
{Module: VaultSeat, ReadsRefused: []string{ControllerLease, GrantsAccounts}},
|
||||||
|
{Module: "mesh-config", ReadsRefused: []string{ControllerLease}},
|
||||||
|
{Module: "restic", ReadsRefused: []string{"disk-load.readings"}},
|
||||||
|
{Module: "messenger", Reads: []string{ControllerLease, GrantsAccounts}}}}})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
reads := map[string]string{}
|
||||||
|
for _, u := range judged {
|
||||||
|
if u.Kind == KindModule {
|
||||||
|
reads[u.Module] = strings.Join(u.Reads, ",")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if reads[VaultSeat] != ControllerLease+","+GrantsAccounts || reads["mesh-config"] != "" || reads["restic"] != "" || reads["messenger"] != "" {
|
||||||
|
t.Errorf("the reads granted for their readers: %v", reads)
|
||||||
|
}
|
||||||
c, _ := ConsumerFor(Principal{Kind: KindModule, Node: "a", Module: "m", Consumes: []string{"x.happened"}})
|
c, _ := ConsumerFor(Principal{Kind: KindModule, Node: "a", Module: "m", Consumes: []string{"x.happened"}})
|
||||||
cfg := ConsumerConfig(c)
|
cfg := ConsumerConfig(c)
|
||||||
if cfg.Durable != "a_m" || cfg.AckPolicy != jetstream.AckExplicitPolicy || cfg.AckWait != 30*time.Second ||
|
if cfg.Durable != "a_m" || cfg.AckPolicy != jetstream.AckExplicitPolicy || cfg.AckWait != 30*time.Second ||
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.novox.be/novox/mesh-sdk/go/announce"
|
||||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||||
"git.novox.be/novox/mesh-sdk/go/handacts"
|
"git.novox.be/novox/mesh-sdk/go/handacts"
|
||||||
"git.novox.be/novox/mesh-sdk/go/health"
|
"git.novox.be/novox/mesh-sdk/go/health"
|
||||||
@@ -116,6 +117,23 @@ func readsNamed(module string, reads []string) (granted, refused []string) {
|
|||||||
return granted, refused
|
return granted, refused
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// withAnnouncedReads is d with its reads judged for d's module (ReadsAnnouncedBy) against what the owners announced:
|
||||||
|
// every read its manifest names, granted or refused by the caller, judged again here, so a caller that split them
|
||||||
|
// without the reader (the controller's own copy of a reader-less judgement, issue 774: the vault, the assigner, the
|
||||||
|
// grants part and the artifact store's tools refused the lease they are named for) cannot refuse a read the reader is
|
||||||
|
// named for, nor grant one named for another. With no announcements read it is withNamedReaders.
|
||||||
|
func withAnnouncedReads(d Declared, announced map[string]announce.States) Declared {
|
||||||
|
if announced == nil {
|
||||||
|
return withNamedReaders(d)
|
||||||
|
}
|
||||||
|
all := append(append([]string(nil), d.Reads...), d.ReadsRefused...)
|
||||||
|
if len(all) == 0 {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
d.Reads, d.ReadsRefused = ReadsAnnouncedBy(d.Module, all, announced)
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
// withNamedReaders is d with the reads named for other modules moved to its refused reads.
|
// withNamedReaders is d with the reads named for other modules moved to its refused reads.
|
||||||
func withNamedReaders(d Declared) Declared {
|
func withNamedReaders(d Declared) Declared {
|
||||||
granted, refused := readsNamed(d.Module, d.Reads)
|
granted, refused := readsNamed(d.Module, d.Reads)
|
||||||
|
|||||||
@@ -205,9 +205,9 @@ func Users(r Records) ([]Principal, error) {
|
|||||||
if !Composed(d) {
|
if !Composed(d) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// A state with named readers is read by them alone (ADR 0547 clause 9): a read named for another module is
|
// A state with named readers is read by them alone (ADR 0547 clause 9), and a read is granted what its owner
|
||||||
// granted nothing, here and in the runtime carrying the module.
|
// announced (ADR 0495 clause 3): judged here, for this reader, whatever split the caller made (issue 774).
|
||||||
d = withReadLogs(withNamedReaders(d), logs)
|
d = withReadLogs(withAnnouncedReads(d, r.Announced), logs)
|
||||||
// **A process account is its own, delivered in its own secret** (broker-<process id>, novox/hq
|
// **A process account is its own, delivered in its own secret** (broker-<process id>, novox/hq
|
||||||
// ADR 0436 §1, §2), never the module's `broker` — so it is composed whether or not the module
|
// ADR 0436 §1, §2), never the module's `broker` — so it is composed whether or not the module
|
||||||
// itself has an account, and is never carried by the machine's runtime, which runs as the
|
// itself has an account, and is never carried by the machine's runtime, which runs as the
|
||||||
@@ -259,7 +259,7 @@ func Users(r Records) ([]Principal, error) {
|
|||||||
var carried []Declared
|
var carried []Declared
|
||||||
for _, d := range r.Assigned[node] {
|
for _, d := range r.Assigned[node] {
|
||||||
if CarriedByRunner(d) {
|
if CarriedByRunner(d) {
|
||||||
d = withReadLogs(withNamedReaders(d), logs)
|
d = withReadLogs(withAnnouncedReads(d, r.Announced), logs)
|
||||||
// Never a restricted stream for what the tool runner carries (novox/hq ADR 0376 §1).
|
// Never a restricted stream for what the tool runner carries (novox/hq ADR 0376 §1).
|
||||||
d.Hears = ComposedHears(d)
|
d.Hears = ComposedHears(d)
|
||||||
carried = append(carried, d)
|
carried = append(carried, d)
|
||||||
|
|||||||
Reference in New Issue
Block a user