Give the controller the contract it can fill (hq ADR 0325, issue 567)
The controller cannot know the operator's account's uid, names an access by its declared id (which holds a slash), and should not parse words. A tool runner's account may now come by name alone and is read here (refused when absent or root's, its uid said in the access's listing); the take act matches the declared id exactly; an access wait says its reason (keep or disk) and, for a filesystem that refuses ACLs, whether the take act can enable them.
This commit is contained in:
+10
-2
@@ -60,6 +60,10 @@ func newAccessJudge() *accessJudge {
|
||||
func (j *accessJudge) set(d *declaration.Declaration, held map[string]bool) {
|
||||
var as []judgedAccess
|
||||
if d != nil {
|
||||
// A tool runner's account that came with no number is read by name, as the apply read it (ADR 0325 §4).
|
||||
ctx, cancel := context.WithTimeout(context.Background(), lookBound)
|
||||
apply.ResolveRunners(ctx, d)
|
||||
cancel()
|
||||
users := map[string]map[uint32]store.KeptUser{}
|
||||
taken := map[string]bool{}
|
||||
for _, r := range d.Resources {
|
||||
@@ -161,11 +165,11 @@ func judgeAccess(a judgedAccess) verdict {
|
||||
return verdict{state: link.StateWaiting, reason: "it holds data the mesh has not taken on the filesystem " +
|
||||
"it is on now, and waits for the operator's word (novox/hq ADR 0325 §7, §8)",
|
||||
waits: []link.AccessWait{{Kind: link.WaitKindAccess, Access: a.name, Part: a.module,
|
||||
What: apply.KeepWords(a.name), Placed: v.Placed}}}
|
||||
What: apply.KeepWords(a.name), Placed: v.Placed, Reason: link.WaitKeep, ACL: v.ACL}}}
|
||||
case apply.VerdictWaitDisk:
|
||||
return verdict{state: link.StateWaiting, reason: "the disk that holds it is not there (novox/hq ADR 0325 §9)",
|
||||
waits: []link.AccessWait{{Kind: link.WaitKindAccess, Access: a.name, Part: a.module,
|
||||
What: apply.DiskWords(a.name)}}}
|
||||
What: apply.DiskWords(a.name), Reason: link.WaitDisk}}}
|
||||
case apply.VerdictRoot:
|
||||
return verdict{state: link.StateUnhealthy, reason: apply.RootWords(v.Why)}
|
||||
case apply.VerdictUnknown:
|
||||
@@ -288,6 +292,10 @@ func (j *accessJudge) look(now time.Time) ([]link.ResourceHealth, bool) {
|
||||
out := make([]link.ResourceHealth, 0, len(as))
|
||||
for i, a := range as {
|
||||
r := read[i]
|
||||
// Every kept or waiting access lists its users, the tool runner's account with the uid read here (§4).
|
||||
if r.entries == nil && len(a.users) > 0 && !a.leftover {
|
||||
r.entries = entriesOf(a.users)
|
||||
}
|
||||
kind := link.KindAccess
|
||||
if a.leftover {
|
||||
kind = link.KindKeptEntries
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -8,6 +9,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/novox/mesh-host/internal/apply"
|
||||
"github.com/novox/mesh-host/internal/declaration"
|
||||
"github.com/novox/mesh-host/internal/link"
|
||||
"github.com/novox/mesh-host/internal/store"
|
||||
@@ -48,7 +50,7 @@ func TestAnAccessTheMeshMayNotKeepYetIsSaidAsAWaitOfItsOwnKind(t *testing.T) {
|
||||
}
|
||||
raw, _ := json.Marshal(v.waits[0])
|
||||
want := `{"kind":"access","access":"movies","part":"radarr","what":"let the mesh keep the shared folder movies",` +
|
||||
`"placed":"` + v.waits[0].Placed + `"}`
|
||||
`"placed":"` + v.waits[0].Placed + `","reason":"keep"}`
|
||||
if string(raw) != want || !strings.HasPrefix(v.waits[0].Placed, "fs-") {
|
||||
t.Fatalf("the wait reads %s", raw)
|
||||
}
|
||||
@@ -76,3 +78,40 @@ func TestAnOlderControllersAccessesSayNothing(t *testing.T) {
|
||||
t.Fatalf("an older controller's accesses are judged: %+v", vs)
|
||||
}
|
||||
}
|
||||
|
||||
// The waits' reasons and the tool runner's uid read here are said without words to parse (ADR 0325 §4, §7, §9).
|
||||
func TestTheWaitsSayTheirReasonAndTheRunnersUid(t *testing.T) {
|
||||
if link.ACLNeedsEnabling != apply.ACLNeedsEnabling || link.ACLUnsupported != apply.ACLUnsupported {
|
||||
t.Fatal("the engine's ACL words are not the link's")
|
||||
}
|
||||
dir := t.TempDir()
|
||||
was := apply.AccountIDs
|
||||
apply.AccountIDs = func(context.Context, string) (int, int, bool, error) { return 61003, 1000, true, nil }
|
||||
defer func() { apply.AccountIDs = was }()
|
||||
d, err := declaration.Parse([]byte(`{"declaration":1,"resources":[{"id":"importer.books","type":"access",` +
|
||||
`"path":"` + filepath.Join(dir, "no", "such") + `","mode":"read-write","users":[{"identity":"nox",` +
|
||||
`"use":"read-write","from":"tool-runner"}]}]}`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
j := newAccessJudge()
|
||||
j.set(d, nil)
|
||||
vs, _ := j.look(time.Now())
|
||||
if len(vs) != 1 || len(vs[0].Entries) != 1 || vs[0].Entries[0].UID != 61003 {
|
||||
t.Fatalf("the tool runner's uid read here is not said: %+v", vs)
|
||||
}
|
||||
// A folder whose disk is not there: its mount target named, not mounted.
|
||||
disk := filepath.Join(dir, "disk")
|
||||
if err := os.Mkdir(disk, 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wasTargets := apply.MountTargets
|
||||
apply.MountTargets = func(context.Context) []string { return []string{disk} }
|
||||
defer func() { apply.MountTargets = wasTargets }()
|
||||
v := judgeAccess(judgedAccess{module: "radarr", id: "radarr.movies", name: "movies",
|
||||
path: filepath.Join(disk, "movies"), users: []store.KeptUser{{Identity: "mesh-radarr", UID: 61001,
|
||||
Use: "read-write", From: "container"}}})
|
||||
if len(v.waits) != 1 || v.waits[0].Reason != link.WaitDisk || link.WaitDisk != "disk" || link.WaitKeep != "keep" {
|
||||
t.Fatalf("a disk wait does not say its reason: %+v", v.waits)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user