A module's containers ran as whatever their image chose and shared folders were made writable by putting accounts in the folder's group (ADR 0312), which ADR 0325 supersedes. The engine now makes each declared module identity as a system account with the controller's numbers, runs a container declaring `identity` as it, and keeps each placed shared folder usable through one POSIX ACL entry per user, set through the kernel's xattrs on descriptors it opened itself. A folder holding data is kept only after the operator's warrant (the take act); a missing folder is made where its data lives and waits where its disk is not; root's data is never kept. A watch and a bounded sweep put back what an app narrows. The group grant is removed, and what it put an account in is taken back. Reuses the descriptor walks, the create act, the missing-path wait and the warrant- backed act message from the paused issue 567 branch.
76 lines
2.7 KiB
Go
76 lines
2.7 KiB
Go
package apply
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/novox/mesh-host/internal/store"
|
|
)
|
|
|
|
// An operator-owned path the module reaches but does not own (novox/hq ADR 0051).
|
|
//
|
|
// The host confirms it is present and changes nothing: it does not create it, chown it or set its
|
|
// mode, because the media library and the download spool are the operator's and several modules
|
|
// share them. This is the opposite of a directory on every axis, and the whole reason the two are
|
|
// different shapes.
|
|
func TestAnAccessPresentIsConfirmedAndNothingIsChanged(t *testing.T) {
|
|
dir := t.TempDir() // the operator's directory, already there
|
|
d := declare(t, `{"id":"lib","type":"access","path":"`+dir+`","mode":"read-write"}`)
|
|
|
|
report, _, err := Apply(context.Background(), archHost(t), d, store.State{},
|
|
store.OriginDeclared, noServices, nil, nil)
|
|
if err != nil {
|
|
t.Fatalf("an operator-owned path that is present was refused: %v", err)
|
|
}
|
|
if report.Changed() {
|
|
t.Fatalf("confirming an access reported a change; the host owns nothing about it")
|
|
}
|
|
if _, statErr := os.Stat(dir); statErr != nil {
|
|
t.Fatalf("the operator's directory was disturbed: %v", statErr)
|
|
}
|
|
}
|
|
|
|
// Undeclaring an access never removes the path. Unassigning the module that reached the media
|
|
// library must not delete the library — that is the data loss ADR 0030 exists to prevent, on a
|
|
// directory the mesh never made. The record is dropped; the operator's data is left exactly as it
|
|
// is.
|
|
func TestAnUndeclaredAccessLeavesTheOperatorsPathAlone(t *testing.T) {
|
|
lib := t.TempDir() // the operator's library
|
|
keep := t.TempDir()
|
|
if err := os.WriteFile(lib+"/a-real-file", []byte("the operator's data"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
d := declare(t, `{"id":"lib","type":"access","path":"`+lib+`","mode":"read"}`)
|
|
_, state, err := Apply(context.Background(), archHost(t), d, store.State{},
|
|
store.OriginDeclared, noServices, nil, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// The module is unassigned: the mesh no longer declares the access.
|
|
empty := declare(t, `{"id":"unrelated","type":"directory","path":"`+keep+`"}`)
|
|
report, _, err := Apply(context.Background(), archHost(t), empty, state,
|
|
store.OriginDeclared, noServices, nil, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if _, statErr := os.Stat(lib); statErr != nil {
|
|
t.Fatalf("the operator's library was removed when the module stopped reaching it: %v", statErr)
|
|
}
|
|
if _, statErr := os.Stat(lib + "/a-real-file"); statErr != nil {
|
|
t.Fatalf("the operator's data was removed: %v", statErr)
|
|
}
|
|
var forgot bool
|
|
for _, o := range report.Outcomes {
|
|
if o.Type == "access" && o.Action == "forgotten" {
|
|
forgot = true
|
|
}
|
|
}
|
|
if !forgot {
|
|
t.Errorf("dropping an access was not reported as forgotten: %+v", report.Outcomes)
|
|
}
|
|
}
|