package broker import ( "sort" "strings" "git.novox.be/novox/mesh-sdk/go/announce" ) // Who may read a module's state is what the module announced (novox/hq ADR 0495 clauses 2 and 3, ADR 0513 §3; issue // 1790). A module says on the bus which states it keeps and which others may read (announce.Subject); the controller // keeps the last word of each (announce.KeptBucket) and grants a reader's `reads: .` only when the owner // announced that state readable. An owner that has announced nothing yet is read as before, from the reader's `reads` // (the transition, until every module announces), and is named as not yet announced. // StatesAnnouncedBucket is the controller's bucket of the last announcement of each module (announce.KeptBucket). const StatesAnnouncedBucket = announce.KeptBucket // controllerReadable are the controller's states others may read: the record mirrors (the packages each node declares // among them, which the pacman module reads, ADR 0507 §5), the chain's buckets, the checks running and the plans building. var controllerReadable = []string{MirrorAssignments, MirrorDeclaredConfig, MirrorPredefined, ChainBuilds, ChainRollouts, ChecksRunning, PlansBuilding, MirrorDeclaredPackages} // ControllerAnnouncement is what the controller announces it keeps (ADR 0495 clause 2): every bucket of its own by its local // name, the ones others may read marked readable. It announces itself, as any module does from its code. func ControllerAnnouncement(node string) announce.States { s := announce.States{Module: ControllerSeat, Node: node} readable := map[string]bool{} for _, n := range controllerReadable { readable[n] = true } for _, bucket := range ControllerBuckets() { local, mine := strings.CutPrefix(bucket, ControllerSeat+"_") if !mine { continue } s.States = append(s.States, announce.State{Name: local, Readable: readable[local]}) } sort.Slice(s.States, func(i, j int) bool { return s.States[i].Name < s.States[j].Name }) return s } // ReadsAnnounced splits a reader's `reads` (`.`) by what each owner announced: granted when the owner // announced the state readable, or announced nothing yet (the transition); refused when the owner announced and did // not announce that state readable. func ReadsAnnounced(reads []string, announced map[string]announce.States) (granted, refused []string) { for _, r := range reads { owner, name, ok := strings.Cut(r, ".") s, said := announced[owner] switch { case !ok || !said || s.Readable(name): granted = append(granted, r) default: refused = append(refused, r) } } return granted, refused } // NotAnnounced are the owners some module reads the state of and that have announced nothing yet, sorted: read from the // readers' `reads` as before, until they announce. func NotAnnounced(r Records) []string { seen := map[string]bool{} var out []string for _, ds := range r.Assigned { for _, d := range ds { for _, read := range append(append([]string(nil), d.Reads...), d.ReadsRefused...) { owner, _, ok := strings.Cut(read, ".") if !ok || seen[owner] { continue } if _, said := r.Announced[owner]; !said { seen[owner] = true out = append(out, owner) } } } } sort.Strings(out) return out }