152 lines
6.1 KiB
Go
152 lines
6.1 KiB
Go
package grants
|
|
|
|
import (
|
|
"fmt"
|
|
"slices"
|
|
"sort"
|
|
"strings"
|
|
|
|
"mesh-grants/internal/buscompose"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/asks"
|
|
)
|
|
|
|
// **A module whose manifest waits or is refused keeps its bus users** (clause 3 of the record on a release's Approve; issue
|
|
// 774). The indexer holds a manifest it refuses, or that waits for a judge that knows a field of it, out of the manifests
|
|
// the part composes from (inputs.go, Refused), so a module with no want naming a build had no manifest to derive its users
|
|
// from and its users left the list: the bus module's accounts among them, which locked its tools out. Now such a module's
|
|
// users, the ones the list in force carries, stay in the list as it holds them, the same way a refused principal's do
|
|
// (refused.go), and the part says so as the warning mesh-grants.module-held until the manifest is accepted. One module's
|
|
// manifest never holds the list: every other principal is composed as ever. A module whose want names a build is composed
|
|
// from the manifest of that build (Inputs.Running), not kept.
|
|
|
|
// HeldModuleCondition is the key of the warning raised while a module's manifest is held and its users are kept.
|
|
const HeldModuleCondition = "mesh-grants.module-held"
|
|
|
|
// heldModuleUsers are, by user, why it is kept: the users of candidates (the ones the list in force and the bus carry) that
|
|
// belong to a module placed on a node whose manifest the indexer holds (in.Refused) and whose want names no build. A user of
|
|
// a module on a node is `<node>.<module>` or `<node>.<module>.<process>` (buscompose.Principal.Username). Pure.
|
|
func heldModuleUsers(in Inputs, candidates []string) map[string]string {
|
|
held := map[string]string{}
|
|
for _, g := range in.Refused {
|
|
if module, ok := strings.CutPrefix(g.Input, "manifest of "); ok {
|
|
held[module] = g.Why
|
|
}
|
|
}
|
|
out := map[string]string{}
|
|
if len(held) == 0 {
|
|
return out
|
|
}
|
|
for node, modules := range in.Assigned {
|
|
for _, module := range modules {
|
|
why, ok := held[module]
|
|
if !ok {
|
|
continue
|
|
}
|
|
if _, runs := in.Running[node+"."+module]; runs {
|
|
continue
|
|
}
|
|
prefix := node + "." + module
|
|
// No manifest of the module can be read at all (the trunk's is refused and no running build's is found): named
|
|
// unreadable, in the condition and in grants_user_list (ADR 0614 §8).
|
|
state := "held"
|
|
if _, readable := in.Held[module]; !readable {
|
|
state = "unreadable"
|
|
}
|
|
for _, user := range candidates {
|
|
if user == prefix || strings.HasPrefix(user, prefix+".") {
|
|
out[user] = fmt.Sprintf("the manifest of %s is %s: %s", module, state, why)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// runnerUsersKept are, by user, why it is kept: on a node where no placed module's manifest says it is the tool runner
|
|
// (`tool-runner`), the user of the list in force that is that node's tool runner, known by what runs (it may say the runner
|
|
// is alive, buscompose.ToolsAliveSubject, which the runner alone is granted), never by a module's name (ADR 0601 §4). A
|
|
// manifest that does not yet say what shared code no longer infers keeps the runner as it runs, said in the held module's
|
|
// condition, and is never composed without it. Pure.
|
|
func runnerUsersKept(recs buscompose.Records, inForce map[string]User) map[string]string {
|
|
out := map[string]string{}
|
|
for node, placed := range recs.Assigned {
|
|
if slices.ContainsFunc(placed, func(d buscompose.Declared) bool { return d.ToolRunner }) {
|
|
continue
|
|
}
|
|
alive := buscompose.ToolsAliveSubject(node)
|
|
for user, u := range inForce {
|
|
if strings.HasPrefix(user, node+".") && slices.Contains(u.Publish, alive) {
|
|
out[user] = fmt.Sprintf("no manifest placed on %s says it is the tool runner (tool-runner), so the tool runner keeps "+
|
|
"what it runs until its manifest says so", node)
|
|
}
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// inForceGrants are the grants of each user of the list in force as the part sealed them, or nil where it kept none.
|
|
func (p *Part) inForceGrants() map[string]User {
|
|
if g, _, had := p.Sealed(); had && g.Digest != "" && p.Store != nil {
|
|
inForce, _ := p.sealedGrantsOf(g.Digest)
|
|
return inForce
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// mergedRefusals is refused and held in one map, for the parts that keep a user's grants of the list in force.
|
|
func mergedRefusals(refused, held map[string]string) map[string]string {
|
|
if len(held) == 0 {
|
|
return refused
|
|
}
|
|
out := make(map[string]string, len(refused)+len(held))
|
|
for u, why := range held {
|
|
out[u] = why
|
|
}
|
|
for u, why := range refused {
|
|
out[u] = why
|
|
}
|
|
return out
|
|
}
|
|
|
|
// sayHeldModules raises HeldModuleCondition while a held module's users are kept, naming each user with what became of it,
|
|
// and clears it once none is; said once per set, again when it changes, and at the first pass after a start.
|
|
func (p *Part) sayHeldModules(held map[string]string, kept map[string]User) {
|
|
if p.Say == nil {
|
|
return
|
|
}
|
|
var lines []string
|
|
users := make([]string, 0, len(held))
|
|
for u := range held {
|
|
users = append(users, u)
|
|
}
|
|
sort.Strings(users)
|
|
for _, user := range users {
|
|
became := "not in the list in force and left out"
|
|
if _, ok := kept[user]; ok {
|
|
became = "stays in the list as the list in force holds it"
|
|
}
|
|
lines = append(lines, user+" ("+became+"): "+held[user])
|
|
}
|
|
why := strings.Join(lines, "; ")
|
|
p.holdMu.Lock()
|
|
defer p.holdMu.Unlock()
|
|
if p.heldModulesKnown && why == p.heldModulesSaid {
|
|
return
|
|
}
|
|
c := asks.Condition{Key: HeldModuleCondition, Subject: asks.Scope{Scope: "module", ID: "mesh-grants"}}
|
|
if why == "" {
|
|
c.State = asks.StateCleared
|
|
} else {
|
|
c.Severity, c.State = asks.SeverityWarning, asks.StateRaised
|
|
c.Summary = fmt.Sprintf("%d bus user(s) of a module whose manifest is held are kept in the list: %s", len(users), why)
|
|
c.Headline, c.Needs = "A module's manifest is held, its bus users are kept", "a manifest the indexer accepts, or a judge that knows its fields"
|
|
c.Explanation = asks.NeedsYou + "a manifest the indexer accepts (a refused one mended, or a waiting one judged by an indexer that knows its fields). " +
|
|
"The rest of the user list is written as ever; a held module's users stay as the list in force holds them: " + why
|
|
c.Resolved = "every module's manifest is accepted"
|
|
}
|
|
if p.Say(c) == nil {
|
|
p.heldModulesSaid, p.heldModulesKnown = why, true
|
|
}
|
|
}
|