diff --git a/cmd/mesh-controller/plan.go b/cmd/mesh-controller/plan.go index 16aa5515..3fd360c7 100644 --- a/cmd/mesh-controller/plan.go +++ b/cmd/mesh-controller/plan.go @@ -947,14 +947,9 @@ func renderingFor(ctx context.Context, open *stores, node string, if err != nil { return catalogue.Rendering{}, inventory.Node{}, err } - grantsGroups, err := engineGrantsGroups(ctx, inv, node) - if err != nil { - return catalogue.Rendering{}, inventory.Node{}, err - } return catalogue.Rendering{ ReadsHealth: readsHealth, JudgesRoot: judgesRoot, - GrantsGroups: grantsGroups, BusMembership: memberships[node], Settings: settings, Generators: gens, Grants: grants, Needed: needed, Foreseen: foreseen, Ports: ports, Certificate: certificate, Authority: authority, Mesh: private, Names: names, @@ -987,16 +982,6 @@ func engineJudgesRoot(ctx context.Context, inv *inventory.Inventory, node string return had && stated.Contract >= link.RootContract, nil } -// engineGrantsGroups says whether a machine's node-engine reads an access's `grant` (novox/hq ADR 0312), by its own -// newest statement, for the same reason as engineReadsHealth: an older engine parses strictly. -func engineGrantsGroups(ctx context.Context, inv *inventory.Inventory, node string) (bool, error) { - stated, had, err := inv.HealthOf(ctx, node) - if err != nil { - return false, err - } - return had && stated.Contract >= link.GrantContract, nil -} - // zonesInTheMesh is every zone a module in the mesh declares, where the mesh placed it (novox/hq ADR // 0199): the zone settled from that node's settings, the node's private address, the port the // answering listen is published on there. diff --git a/go.mod b/go.mod index 527faf8e..cdf300d5 100644 --- a/go.mod +++ b/go.mod @@ -35,4 +35,4 @@ require ( // committed. Every build (the build agent's `go build`, the Dockerfile) compiles from vendor/ and // fetches nothing; go refuses to build when vendor/ and this file disagree, so a pin moved without // `go mod vendor` fails loudly, at once, everywhere. -replace github.com/novox/mesh-host => git.novox.be/novox/mesh-host v0.0.0-20261012112256-3f7f797168d3 +replace github.com/novox/mesh-host => git.novox.be/novox/mesh-host v0.0.0-20261011092230-20d5af9b2d5f diff --git a/go.sum b/go.sum index dfc79fdf..e7436cbb 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -git.novox.be/novox/mesh-host v0.0.0-20261012112256-3f7f797168d3 h1:VpH8CesuBOo7k8GcXZq1eRUfbvUoktfmcUynI5N3IFQ= -git.novox.be/novox/mesh-host v0.0.0-20261012112256-3f7f797168d3/go.mod h1:tcTK4LMs1d6JpZUwy3hSHMFG/MIuDr/XFs7/nbeaW/c= +git.novox.be/novox/mesh-host v0.0.0-20261011092230-20d5af9b2d5f h1:8N5OW2mdTNIck2pe4EciTYX5NsrPsHrTLENGNIWYNTU= +git.novox.be/novox/mesh-host v0.0.0-20261011092230-20d5af9b2d5f/go.mod h1:tcTK4LMs1d6JpZUwy3hSHMFG/MIuDr/XFs7/nbeaW/c= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261010191001-33917f91ac3c h1:l5onJwoIeH8yE/PjVlEeoPyXBsHp2NQiWLllz2fwX7s= git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261010191001-33917f91ac3c/go.mod h1:GFuZUElBZ9A++mxgIKo97aXXo+kV0uJ/UkbhQPPIbrY= github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0= diff --git a/internal/catalogue/declaration.go b/internal/catalogue/declaration.go index facc148e..a74ca6dc 100644 --- a/internal/catalogue/declaration.go +++ b/internal/catalogue/declaration.go @@ -246,11 +246,6 @@ type Rendering struct { // statement's contract is link.RootContract or later). To an older, strict engine the field is not // sent, and the account it names is not judged — which the self-check says, as not judged. JudgesRoot bool - - // GrantsGroups says this machine's node-engine reads an access's `grant` (novox/hq ADR 0312: its statement's - // contract is link.GrantContract or later). To an older, strict engine the field is not sent, nor the - // runtime's restarts on it, and a read-write access is reached as before: by whoever owns the path. - GrantsGroups bool } // RootField is a user resource's field saying the account must never become root without a person @@ -548,9 +543,6 @@ func (r Resolution) compose(with Rendering, owner map[string]string, } var out []map[string]any - // The read-write accesses each module grants its tools' account, by module (novox/hq ADR 0312): the runtime - // that runs as that account is restarted on them. - grantedToTools := map[string][]string{} for _, m := range r.Modules { if with.Adopted && m.Filtering != nil { // Nothing of a module that loads a filter, on an adopted node: its table would drop @@ -1123,13 +1115,6 @@ func (r Resolution) compose(with Rendering, owner map[string]string, out = append(out, copied) } - // **A read-write access grants its path's group to what runs this module's code** (novox/hq ADR 0312): - // the containers composed above that mount it, and the account its tools run as. Only to an engine that - // reads the field. - if with.GrantsGroups { - grantedToTools[m.Module] = grantAccesses(out, owner, m.Module, r.toolsAccount(m, with)) - } - // **What only the mesh knows, where this module asked for it.** The graph is the control // plane's; making a name resolve is the module's software. Emitted as ordinary files under // this module's name, so they are applied, reported and removed exactly as anything else @@ -1156,12 +1141,6 @@ func (r Resolution) compose(with Rendering, owner map[string]string, } owner[fmt.Sprint(process["id"])] = RuntimeModule out = append(out, process) - // Restarted when an access moved its account's groups, so its process holds them (novox/hq ADR 0312 §6). - for _, m := range r.Modules { - if m.RunsAs == "" { - restartOnGrants(process, grantedToTools[m.Module]) - } - } // And a runtime of its own for each module of its own account, after it (novox/hq ADR 0259 §8). owns, err := r.ownRuntimes(with) if err != nil { @@ -1170,7 +1149,6 @@ func (r Resolution) compose(with Rendering, owner map[string]string, for _, p := range owns { id := fmt.Sprint(p["id"]) owner[id] = strings.TrimSuffix(id, "."+OwnRuntimeID()) - restartOnGrants(p, grantedToTools[owner[id]]) out = append(out, p) } // What each module's bundles are given is read as the account the runtime runs as — not what a diff --git a/internal/catalogue/grants.go b/internal/catalogue/grants.go deleted file mode 100644 index 2574bdb7..00000000 --- a/internal/catalogue/grants.go +++ /dev/null @@ -1,134 +0,0 @@ -package catalogue - -import ( - "fmt" - "path/filepath" - "sort" - "strings" -) - -// A read-write access grants the path's own group to what runs the module's code (novox/hq ADR 0312, issue 537). -// -// **The controller says who; the node-engine reads which group.** Which group owns the operator's path is a fact -// of the machine the controller cannot know, and the path is never changed (§2): so each read-write access -// reaches a machine naming the identities its module's code runs as, and the node-engine reads the path's group -// at apply and grants it to them. Two kinds of identity run a module's code here: -// -// - each of its containers that mounts the path — the source of one of its volumes is the path, or below it — -// which gains the group as a supplementary group of its process and is recreated with it; -// - the account its tools run as, where it has a tools bundle the machine's runtime loads: the operator's -// account the runtime runs as, or the module's own account for a module served by a runtime of its own -// (ADR 0259 §8). That runtime names the access under its `restart-on`, so it is restarted when the access -// moved the account's groups, and its new process holds the group with no new login (§6). -// -// A read access grants nothing (§3): it is sent as before. A read-write access whose module runs nothing here is -// sent an empty grant, so the node-engine still states it unhealthy while its group cannot write (§2). -// -// **Only to an engine that reads it.** An older node-engine parses strictly and refuses a whole declaration for a -// field it does not know; the field and the restarts it brings are composed only where the engine's statement -// says link.GrantContract or later (Rendering.GrantsGroups). - -// GrantField is an access's field naming who its path's group is granted to (novox/hq ADR 0312). -const GrantField = "grant" - -// toolsAccount is the account module m's tools run as on this machine, or empty where they run nowhere as one: -// no runtime here, no bundle it loads, a module left out of an adopted machine's declaration, or a runtime -// running as root on a machine with no account. -func (r Resolution) toolsAccount(m Manifest, with Rendering) string { - if !r.runtimeHere() || (with.Adopted && m.Filtering != nil) { - return "" - } - loads := false - for _, b := range m.Bundles { - if len(b.Loads) > 0 { - loads = true - } - } - if !loads { - return "" - } - if m.RunsAs != "" { - return m.RunsAs - } - return r.Account -} - -// grantAccesses writes the grant of every read-write access of module, among the resources composed so far: the -// module's containers that mount the access's path, and account when it is not empty. The ids of the accesses -// that grant to account, for the runtime that runs as it to be restarted on. -func grantAccesses(out []map[string]any, owner map[string]string, module, account string) []string { - var containers []map[string]any - for _, res := range out { - if res["type"] == "container" && owner[fmt.Sprint(res["id"])] == module { - containers = append(containers, res) - } - } - var toAccount []string - for _, res := range out { - if res["type"] != "access" || owner[fmt.Sprint(res["id"])] != module || res["mode"] != AccessReadWrite { - continue - } - path, _ := res["path"].(string) - grant := map[string]any{} - var mounting []any - for _, c := range containers { - if mounts(c, path) { - mounting = append(mounting, fmt.Sprint(c["id"])) - } - } - if len(mounting) > 0 { - grant["containers"] = mounting - } - if account != "" { - grant["account"] = account - toAccount = append(toAccount, fmt.Sprint(res["id"])) - } - res[GrantField] = grant - } - sort.Strings(toAccount) - return toAccount -} - -// mounts says whether a container mounts path, or something below it, from the machine. A path that merely -// begins with the same letters — /storage/media2 beside /storage/media — is another path. A read-only mount of a -// read-write access still counts: the module declared the access read-write, the engine grants the group to what -// mounts it, and a read-only mount only means that container chose not to write through it. -func mounts(container map[string]any, path string) bool { - volumes, _ := container["volumes"].([]any) - path = filepath.Clean(path) - for _, v := range volumes { - s, ok := v.(string) - if !ok { - continue - } - source, _, _ := strings.Cut(s, ":") - if !strings.HasPrefix(source, "/") { - continue // a named volume: the runtime's, not the operator's path - } - source = filepath.Clean(source) - if source == path || strings.HasPrefix(source, path+"/") { - return true - } - } - return false -} - -// restartOnGrants adds to a runtime process's restart-on the accesses that grant to the account it runs as, so it -// is restarted to hold a group the account was put in or taken out of (novox/hq ADR 0312 §6). -func restartOnGrants(process map[string]any, ids []string) { - if process == nil || len(ids) == 0 { - return - } - restarts, _ := process["restart-on"].([]any) - seen := map[string]bool{} - for _, id := range restarts { - seen[fmt.Sprint(id)] = true - } - for _, id := range ids { - if !seen[id] { - restarts = append(restarts, id) - seen[id] = true - } - } - process["restart-on"] = restarts -} diff --git a/internal/catalogue/grants_test.go b/internal/catalogue/grants_test.go deleted file mode 100644 index 382b8e68..00000000 --- a/internal/catalogue/grants_test.go +++ /dev/null @@ -1,174 +0,0 @@ -package catalogue - -import ( - "encoding/json" - "fmt" - "testing" - - "github.com/novox/mesh-host/validate" -) - -// Defends novox/hq ADR 0312 and issue 537 on the controller's side: each read-write access reaches the machine -// naming who its path's group is granted to — the module's containers that mount it, and the account its tools -// run as — and the tool runner is restarted on it; a read access grants nothing; and none of it is sent to a -// node-engine older than the field, which would refuse the whole declaration. - -const anImage = "x@sha256:1111111111111111111111111111111111111111111111111111111111111111" - -// anImporter files into one library it writes and reads another: a container mounting both, a step mounting -// neither, and tools served by the machine's runtime. -func anImporter(t *testing.T) Manifest { - t.Helper() - m := aToolsModule(t, "importer", "tools/index.js") - m.Accesses = []Access{ - {ID: "music", Path: "/storage/media/music", Mode: AccessReadWrite}, - {ID: "movies", Path: "/storage/media/movies", Mode: AccessRead}, - } - m.Resources = []map[string]any{ - {"id": "app", "type": "container", "name": "importer", "image": anImage, - "volumes": []any{"${access:music}/incoming:/music", "${access:movies}:/movies:ro"}}, - {"id": "sidecar", "type": "container", "name": "importer-sidecar", "image": anImage}, - } - return m -} - -func composeImporter(t *testing.T, r Resolution, with Rendering) map[string]map[string]any { - t.Helper() - with.Needed = map[string]map[string]string{RuntimeModule: {"broker": "sealed-credential"}} - with.ArtifactStore = "anchor.internal:5101" - out, err := r.Declaration(with) - if err != nil { - t.Fatal(err) - } - // What the node-engine this controller pins would make of it: nothing refused. - body, err := json.Marshal(map[string]any{"declaration": validate.Version, "resources": out}) - if err != nil { - t.Fatal(err) - } - if problems := validate.Declaration(body); len(problems) > 0 { - t.Fatalf("composed into something the node-engine refuses: %v", problems) - } - return byID(out) -} - -func TestAReadWriteAccessNamesTheContainersThatMountItAndTheToolsAccount(t *testing.T) { - r := Resolution{Node: "ace", Account: "jochen", Modules: []Manifest{anImporter(t), theRuntime(t)}} - got := composeImporter(t, r, Rendering{GrantsGroups: true}) - - music := got["importer."+AccessID("/storage/media/music")] - if music == nil { - t.Fatalf("no access resource for the library: %v", keysOf(got)) - } - grant, _ := music[GrantField].(map[string]any) - if fmt.Sprint(grant["containers"]) != "[importer.app]" || grant["account"] != "jochen" { - t.Errorf("the read-write access grants %v; want the container that mounts it and the tools' account", grant) - } - // A read access grants nothing (§3). - if movies := got["importer."+AccessID("/storage/media/movies")]; movies[GrantField] != nil { - t.Errorf("a read access names a grant: %v", movies[GrantField]) - } - // The tool runner is restarted when the access moved its account's groups (§6). - runtime := got[RuntimeModule+"."+RuntimeProcessID()] - found := false - for _, id := range runtime["restart-on"].([]any) { - if id == "importer."+AccessID("/storage/media/music") { - found = true - } - } - if !found { - t.Errorf("the tool runner is not restarted on the access that grants its account: %v", runtime["restart-on"]) - } -} - -func TestAModuleOfItsOwnAccountIsGrantedAsThatAccount(t *testing.T) { - m := anImporter(t) - m.RunsAs = "importer" - m.OwnSecrets = OwnSecrets{"broker": {Path: "/var/lib/mesh/importer/broker"}} - runtime := theRuntime(t) - runtime.Bundles[0].Binary = "node-tools" - r := Resolution{Node: "ace", Account: "jochen", Modules: []Manifest{m, runtime}} - with := Rendering{GrantsGroups: true} - with.Needed = map[string]map[string]string{RuntimeModule: {"broker": "sealed"}, "importer": {"broker": "sealed"}} - with.ArtifactStore = "anchor.internal:5101" - out, err := r.Declaration(with) - if err != nil { - t.Fatal(err) - } - got := byID(out) - grant, _ := got["importer."+AccessID("/storage/media/music")][GrantField].(map[string]any) - if grant["account"] != "importer" { - t.Errorf("the access grants %v; want the module's own account", grant) - } - own := got["importer."+OwnRuntimeID()] - if fmt.Sprint(own["restart-on"]) == "" || !restartsOnIt(own["restart-on"], "importer."+AccessID("/storage/media/music")) { - t.Errorf("the module's own runtime is not restarted on its access: %v", own["restart-on"]) - } - if restartsOnIt(got[RuntimeModule+"."+RuntimeProcessID()]["restart-on"], "importer."+AccessID("/storage/media/music")) { - t.Error("the machine's runtime is restarted on an access of a module it does not serve") - } -} - -func TestAModuleThatRunsNothingHereSendsAnEmptyGrant(t *testing.T) { - m := mod("spool", nil, nil, nil) - m.Accesses = []Access{{Path: "/storage/downloads", Mode: AccessReadWrite}} - out, err := Resolution{Node: "ace", Account: "jochen", Modules: []Manifest{m}}.Declaration(Rendering{GrantsGroups: true}) - if err != nil { - t.Fatal(err) - } - grant, has := byID(out)["spool."+AccessID("/storage/downloads")][GrantField].(map[string]any) - if !has || len(grant) != 0 { - t.Errorf("a read-write access of a module running nothing here is sent %v (sent %v); want an empty grant, "+ - "so its group is still judged", grant, has) - } -} - -func TestNoGrantIsSentToAnEngineOlderThanTheField(t *testing.T) { - r := Resolution{Node: "ace", Account: "jochen", Modules: []Manifest{anImporter(t), theRuntime(t)}} - got := composeImporter(t, r, Rendering{}) - for id, res := range got { - if _, has := res[GrantField]; has { - t.Errorf("%s names a grant to an engine that does not read it", id) - } - } - if restartsOnIt(got[RuntimeModule+"."+RuntimeProcessID()]["restart-on"], "importer."+AccessID("/storage/media/music")) { - t.Error("the tool runner is restarted on an access an older engine grants nothing for") - } -} - -func restartsOnIt(list any, want string) bool { - items, _ := list.([]any) - for _, i := range items { - if fmt.Sprint(i) == want { - return true - } - } - return false -} - -func keysOf(m map[string]map[string]any) []string { - var out []string - for k := range m { - out = append(out, k) - } - return out -} - -// Only a container that mounts the access's path, or below it, is named: a path that begins with the same letters is -// another path. A read-only mount of a read-write access is still named — the access is read-write, and the grant -// follows what mounts it, not how. -func TestOnlyAContainerMountingThePathIsNamedReadOnlyIncluded(t *testing.T) { - m := mod("library", nil, nil, nil) - m.Accesses = []Access{{ID: "media", Path: "/storage/media", Mode: AccessReadWrite}} - m.Resources = []map[string]any{ - {"id": "beside", "type": "container", "name": "beside", "image": anImage, "volumes": []any{"/storage/media2:/x"}}, - {"id": "reader", "type": "container", "name": "reader", "image": anImage, "volumes": []any{"${access:media}:/media:ro"}}, - } - out, err := Resolution{Node: "ace", Modules: []Manifest{m}}.Declaration(Rendering{GrantsGroups: true}) - if err != nil { - t.Fatal(err) - } - grant, _ := byID(out)["library."+AccessID("/storage/media")][GrantField].(map[string]any) - if fmt.Sprint(grant["containers"]) != "[library.reader]" { - t.Errorf("the access grants %v; want the read-only mount and not /storage/media2", grant) - } -} diff --git a/internal/link/protocol.go b/internal/link/protocol.go index 5b431422..63349ece 100644 --- a/internal/link/protocol.go +++ b/internal/link/protocol.go @@ -436,12 +436,6 @@ const ReadinessContract = 2 // parses strictly and would refuse the whole declaration for it. const RootContract = 3 -// GrantContract is the statement of an engine that also reads an access's `grant` (novox/hq ADR 0312): it grants a -// read-write access's path group to the containers and the account the grant names, takes it back, and states the -// access unhealthy while the group cannot write. Only to such an engine is the field sent: an older one parses -// strictly and would refuse the whole declaration for it. -const GrantContract = 4 - // ReasonRoot starts the reason of an account verdict that found a way to root (ADR 0266); the node-engine's // own words (mesh-host internal/accounts ReasonRoot). const ReasonRoot = "can become root without a person" @@ -493,11 +487,6 @@ const KindAccount = "account" // ReasonUsedAsFound. const KindDirectory = "directory" -// KindAccess is a read-write access that grants its path's group (novox/hq ADR 0312): healthy while the group may -// write the path, unhealthy while it cannot — the operator's to decide, since the mesh changes nothing on the path -// — or while the path is owned by a group that is never granted. The node-engine's own (mesh-host internal/link). -const KindAccess = "access" - // ReasonUsedAsFound starts the reason of a directory used as found. const ReasonUsedAsFound = "used as found:" diff --git a/testdata/beside/CAPTURED b/testdata/beside/CAPTURED index c110ebf7..a37baf3b 100644 --- a/testdata/beside/CAPTURED +++ b/testdata/beside/CAPTURED @@ -8,7 +8,7 @@ private addresses. That is acceptable while mesh-controller is private; if it ev a second place to clean besides the catalogue itself. mesh-catalog b9de001833b1b61b297182b8e3bcdae1cbdeace9 modules/*/module.json, modules/nats/Dockerfile -mesh-host 3f7f797168d33ec22241bdd6ef2c64de86eea914 examples/foundation-first-node-nats.lock +mesh-host a76baa4b47f07a8b2bab065603414530bd02b53a examples/foundation-first-node-nats.lock mesh-sdk 33917f91ac3cc213b879b32f3c70e2aa70395f98 conformance/events/module-event.json To move them, from this repository's root, with the two repositories checked out beside it: diff --git a/vendor/github.com/novox/mesh-host/internal/declaration/declaration.go b/vendor/github.com/novox/mesh-host/internal/declaration/declaration.go index e2f6d294..50db892e 100644 --- a/vendor/github.com/novox/mesh-host/internal/declaration/declaration.go +++ b/vendor/github.com/novox/mesh-host/internal/declaration/declaration.go @@ -464,35 +464,6 @@ type Access struct { Path string `json:"path"` // Mode is how this module reaches the path: read or read-write. Absent narrows to read. Mode string `json:"mode,omitempty"` - - // Grant is who the path's own group is granted to, for a read-write access (novox/hq ADR 0312 §1): the - // identities the module's code runs as. The controller says who; the node-engine reads which group owns - // the path when it applies, because only the machine knows, and never changes the path (§2). Absent is a - // controller older than the grant, or an access that grants nothing — and a read access always is one (§3). - Grant *AccessGrant `json:"grant,omitempty"` -} - -// AccessGrant is the identities a read-write access grants its path's group to (novox/hq ADR 0312 §1). -type AccessGrant struct { - // Containers are the module's containers that mount the path, by id: each is created with the group as a - // supplementary group of its process, and recreated when the group moves (§6). - Containers []string `json:"containers,omitempty"` - // Account is the account that runs the module's tools: put in the group as an account group, as ADR 0252 - // §1 puts one, and taken out again as §3 takes back (§5). Empty for a module whose code runs nowhere as - // an account. - Account string `json:"account,omitempty"` -} - -// UnmarshalJSON reads a grant as strictly as a resource is read: a field the engine does not know is a -// thing the controller believes it asked for, and is refused rather than ignored. -func (g *AccessGrant) UnmarshalJSON(raw []byte) error { - type plain AccessGrant - var p plain - if err := strictDecode(raw, &p); err != nil { - return fmt.Errorf("a grant: %w", err) - } - *g = AccessGrant(p) - return nil } func (a *Access) Identity() string { return a.ID } @@ -511,24 +482,6 @@ func (a *Access) validate(where string, _ bool) []string { problems = append(problems, fmt.Sprintf( "%s: an access is %q or %q, not %q", where, AccessRead, AccessReadWrite, a.Mode)) } - if a.Grant != nil && a.Mode != AccessReadWrite { - // A read access reads as any account may, and gains no group (novox/hq ADR 0312 §3). - problems = append(problems, where+": a read access grants nothing, and this one names a grant; "+ - "only a read-write access grants its path's group (novox/hq ADR 0312 §3)") - } - if a.Grant != nil { - for _, id := range a.Grant.Containers { - if id == "" { - problems = append(problems, where+": a grant names a container with no id") - } - } - if a.Grant.Account != "" && !accountName.MatchString(a.Grant.Account) { - // Handed to id, usermod and gpasswd as an argument: a name starting with a dash would be read as an - // option by each of them. - problems = append(problems, fmt.Sprintf("%s: a grant's account is an account's name — letters, digits, "+ - "dots, underscores and dashes, not starting with a dash — and %q is not one", where, a.Grant.Account)) - } - } return problems } @@ -1715,7 +1668,6 @@ func parse(raw []byte, allowActions bool) (*Declaration, error) { d.Resources = append(d.Resources, resource) } problems = append(problems, checkWhileStopped(d.Resources)...) - problems = append(problems, checkGrants(d.Resources)...) problems = append(problems, checkAdoption(env.Adoption, d.Resources, allowActions)...) problems = append(problems, checkReplaces(d.Resources)...) if env.Adoption == nil { @@ -1780,36 +1732,6 @@ func checkWhileStopped(resources []Resource) []string { return problems } -// accountName is what a grant's account may be: the portable account name the user tools take, optionally ending -// in `$`, never starting with a dash. -var accountName = regexp.MustCompile(`^[A-Za-z0-9_.][A-Za-z0-9_.-]*\$?$`) - -// checkGrants judges each access's grant against the whole declaration (novox/hq ADR 0312): a container it -// names is a container of this declaration, as a maintenance window's are. A grant to a container the machine -// is not told about would be a group given to nothing, said as granted. -func checkGrants(resources []Resource) []string { - containers := map[string]bool{} - for _, r := range resources { - if r.Kind() == TypeContainer { - containers[r.Identity()] = true - } - } - var problems []string - for _, r := range resources { - a, ok := r.(*Access) - if !ok || a.Grant == nil { - continue - } - for _, id := range a.Grant.Containers { - if id != "" && !containers[id] { - problems = append(problems, fmt.Sprintf("resource %q: its grant names %q, and this declaration "+ - "has no container by that id", a.ID, id)) - } - } - } - return problems -} - func strictDecode(raw []byte, into any) error { // DisallowUnknownFields is the whole point rather than strictness for its own sake: a // field the host does not know is a thing the control plane believes it asked for. diff --git a/vendor/modules.txt b/vendor/modules.txt index 470b8478..bb6a448e 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -78,7 +78,7 @@ github.com/nats-io/nkeys # github.com/nats-io/nuid v1.0.1 ## explicit github.com/nats-io/nuid -# github.com/novox/mesh-host v0.0.0 => git.novox.be/novox/mesh-host v0.0.0-20261012112256-3f7f797168d3 +# github.com/novox/mesh-host v0.0.0 => git.novox.be/novox/mesh-host v0.0.0-20261011092230-20d5af9b2d5f ## explicit; go 1.26.0 github.com/novox/mesh-host/internal/declaration github.com/novox/mesh-host/rootsearch @@ -135,4 +135,4 @@ golang.org/x/text/width # golang.org/x/time v0.15.0 ## explicit; go 1.25.0 golang.org/x/time/rate -# github.com/novox/mesh-host => git.novox.be/novox/mesh-host v0.0.0-20261012112256-3f7f797168d3 +# github.com/novox/mesh-host => git.novox.be/novox/mesh-host v0.0.0-20261011092230-20d5af9b2d5f