A module may invoke tools, and a manifest is checked where it is written
invokes: a manifest word that becomes exactly the publish grant a person's account gets (ADR 0152), derived by the same composition; refused at parse when it names no tool. module check <file|dir>... runs what registration runs with no store, for a manifest in any repository (hq issue 148).
This commit is contained in:
@@ -42,6 +42,11 @@ var renamed = map[string]string{
|
||||
|
||||
var name = regexp.MustCompile(`^[a-z0-9][a-z0-9-]*(\.[a-z0-9][a-z0-9-]*)*$`)
|
||||
|
||||
// toolName is what a module calls one of its tools: the sdk's tools are `catalog_modules` and
|
||||
// `gitea_list_repos`, so an underscore is ordinary here and a dot is not — the dot is what separates
|
||||
// the module from the tool in `<module>.<tool>`, and a tool name carrying one would be two grants.
|
||||
var toolName = regexp.MustCompile(`^[a-z0-9][a-z0-9_-]*$`)
|
||||
|
||||
// Claim is a singular resource a module takes over.
|
||||
type Claim struct {
|
||||
Name string `json:"name"`
|
||||
@@ -247,6 +252,16 @@ type Manifest struct {
|
||||
// module claiming a seat answers what that seat's protocol promises (novox/hq ADR 0118).
|
||||
Tools []string `json:"tools,omitempty"`
|
||||
|
||||
// Invokes are the tools this module calls, each `<module>.<tool>`, or the single entry `*` for
|
||||
// every tool on the mesh (novox/hq ADR 0152).
|
||||
//
|
||||
// **A grant, and only a grant.** The bus lets this module publish exactly those tool subjects
|
||||
// and nothing beside them — no event, no subscription, no seat. A module that declares none
|
||||
// calls nothing, which is every module but the console today. ADR 0095 made the control plane
|
||||
// the one caller and deferred this until a consumer asked; the console is that consumer, and a
|
||||
// person's account (design 25 §7) already had the same shape.
|
||||
Invokes []string `json:"invokes,omitempty"`
|
||||
|
||||
// Capabilities the machine must have. A different field from Requires because the remedy
|
||||
// differs: a missing module can be assigned, and a missing capability means the wrong
|
||||
// machine.
|
||||
@@ -1290,6 +1305,7 @@ func ParseManifest(raw []byte) (Manifest, error) {
|
||||
"%s listens on %d over %q, which is tcp or udp", m.Module, l.Port, p))
|
||||
}
|
||||
}
|
||||
problems = append(problems, invokeProblems(m)...)
|
||||
problems = append(problems, endpointNameProblems(m)...)
|
||||
problems = append(problems, RouteProblems(m)...)
|
||||
for _, port := range m.Guards {
|
||||
@@ -1766,3 +1782,25 @@ func EndpointPort(m Manifest, name string) (int, bool) {
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// invokeProblems judges what a module says it calls (novox/hq ADR 0152).
|
||||
//
|
||||
// Refused here, in the manifest's words, rather than at the next composition of the bus's user
|
||||
// list — where a bad entry would stop the whole file being written for everybody, as a person's
|
||||
// malformed grant would have (operator.go). An entry that names a module and no tool is the one
|
||||
// mistake worth naming: `shop` reads like a grant to a module's tools and would be a grant to nothing.
|
||||
func invokeProblems(m Manifest) []string {
|
||||
var problems []string
|
||||
for _, t := range m.Invokes {
|
||||
if t == "*" {
|
||||
continue
|
||||
}
|
||||
module, tool, named := strings.Cut(t, ".")
|
||||
if !named || !name.MatchString(module) || !toolName.MatchString(tool) {
|
||||
problems = append(problems, fmt.Sprintf(
|
||||
"%s invokes %q, which does not name a tool: a module invokes <module>.<tool>, or "+
|
||||
"* for every tool on the mesh (novox/hq ADR 0152)", m.Module, t))
|
||||
}
|
||||
}
|
||||
return problems
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user