Unify trunk on main: initialization → main #3

Merged
jschoubben merged 58 commits from initialization into main 2026-09-05 01:13:33 +00:00
2 changed files with 102 additions and 1 deletions
Showing only changes of commit f48e06473d - Show all commits
+31 -1
View File
@@ -601,7 +601,37 @@ func applyService(ctx context.Context, sys system.System, r *declaration.Service
// installed would be describing an effect it declined to have.
func remove(ctx context.Context, sys system.System, a store.Applied, run Runner) (string, string, error) {
switch declaration.Type(a.Type) {
case declaration.TypeFile, declaration.TypeDirectory:
case declaration.TypeDirectory:
// **A directory with anything left in it is kept, and that is the rule that protects
// data.** Everything the mesh put inside is itself a declared resource, and orphans are
// removed in reverse declaration order — so by the time a directory comes to be removed,
// what the mesh wrote there is already gone. Anything still present is something nobody
// declared: a database's files, a mail spool, somebody's uploads.
//
// Before this, unassigning a module deleted its data directory and everything under it,
// and the report said "removed". Nothing anywhere said what had been in there.
//
// This is the host's own line, applied to the one shape where getting it wrong is not
// recoverable: it removes what it made and leaves what it merely configured. An empty
// directory is what it made. A full one is not.
entries, err := os.ReadDir(a.Target)
if errors.Is(err, os.ErrNotExist) {
return "forgotten", "no longer there", nil
}
if err != nil {
return "", "", err
}
if len(entries) > 0 {
return "kept", fmt.Sprintf(
"no longer declared, and %d item(s) inside that the mesh did not put there — "+
"remove it by hand once you know what it is", len(entries)), nil
}
if err := os.Remove(a.Target); err != nil {
return "", "", err
}
return "removed", "no longer declared, and empty", nil
case declaration.TypeFile:
if err := os.RemoveAll(a.Target); err != nil {
return "", "", err
}
+71
View File
@@ -1360,3 +1360,74 @@ func TestResourcesAreAppliedInTheOrderTheyWereDeclared(t *testing.T) {
"another has no way to say so", ran)
}
}
// **A data directory is never removed by the mesh.** The one failure in this system that cannot
// be undone.
//
// Unassigning a module made its directory an orphan, and an orphan directory was deleted with
// everything under it — a database's files, a mail spool, somebody's uploads — and the report
// said "removed". Reproduced before it was fixed: a module was assigned, a service wrote into
// its directory, the module was unassigned, and the file was gone.
//
// What makes the rule safe rather than merely cautious is the removal order. Everything the mesh
// puts in a directory is itself a declared resource, and orphans are removed in reverse
// declaration order — so what the mesh wrote is already gone by the time the directory is
// reached. Anything still there was put there by something else.
func TestADirectoryHoldingAnythingTheMeshDidNotPutThereIsKept(t *testing.T) {
root := t.TempDir()
data := filepath.Join(root, "keycloak")
d := declare(t, `{"id":"data","type":"directory","path":"`+data+`","mode":"0700"}`)
_, state, err := Apply(context.Background(), archHost(t), d, store.State{},
store.OriginDeclared, nil, nil, nil)
if err != nil {
t.Fatal(err)
}
live := filepath.Join(data, "realm.db")
if err := os.WriteFile(live, []byte("everybody's logins"), 0o600); err != nil {
t.Fatal(err)
}
// The module is unassigned: the mesh declares something else entirely.
after := declare(t, `{"id":"other","type":"directory","path":"`+filepath.Join(root, "other")+`","mode":"0700"}`)
report, _, err := Apply(context.Background(), archHost(t), after, state,
store.OriginDeclared, nil, nil, nil)
if err != nil {
t.Fatal(err)
}
if _, err := os.Stat(live); err != nil {
t.Fatalf("the data was deleted by unassigning a module: %v", err)
}
// And it is said, rather than left for somebody to notice. A directory quietly left behind is
// how a machine accumulates things nobody can account for.
var said bool
for _, o := range report.Outcomes {
if o.Action == "kept" && strings.Contains(o.Detail, "did not put there") {
said = true
}
}
if !said {
t.Errorf("the directory was kept and nothing reported it: %+v", report.Outcomes)
}
}
// An empty one is the mesh's own, and goes.
func TestAnEmptyDirectoryIsStillRemoved(t *testing.T) {
root := t.TempDir()
mine := filepath.Join(root, "config")
d := declare(t, `{"id":"c","type":"directory","path":"`+mine+`","mode":"0700"}`)
_, state, err := Apply(context.Background(), archHost(t), d, store.State{},
store.OriginDeclared, nil, nil, nil)
if err != nil {
t.Fatal(err)
}
after := declare(t, `{"id":"other","type":"directory","path":"`+filepath.Join(root, "other")+`","mode":"0700"}`)
if _, _, err := Apply(context.Background(), archHost(t), after, state,
store.OriginDeclared, nil, nil, nil); err != nil {
t.Fatal(err)
}
if _, err := os.Stat(mine); !errors.Is(err, os.ErrNotExist) {
t.Fatal("an empty directory the mesh made was left behind, so nothing is ever cleaned up")
}
}