Hold a record-policy move that changes a non-file/directory resource (issue 785)

ADR 0377 §3's allowlist names only tools bundles, files no container mounts
and holds no data, and manifest fields that recreate nothing. Holds only
checked containers, files, directories and processes/services; any other
resource type (archive, package, user, network, ...) fell through unheld.
Add a default case that holds and names the resource type and id.
This commit is contained in:
jochen
2026-10-15 14:59:06 +02:00
parent 15a374659e
commit 867a3f81a9
2 changed files with 11 additions and 1 deletions
+5 -1
View File
@@ -23,7 +23,9 @@ func TestWhatAMoveUnderRecordHoldsFor(t *testing.T) {
{"id":"inbox-note","type":"file","path":"${dir:data-mail}/README","content":"x"},
{"id":"imap","type":"container","image":"registry/imap@sha256:1","volumes":["${dir:data-mail}:/mail"],
"env-file":["${dir:state}/mailu.env"]},
{"id":"watcher","type":"process","command":"watch"}]}`
{"id":"watcher","type":"process","command":"watch"},
{"id":"installation","type":"archive","path":"/opt/mailu-tools","artifact":"archive"},
{"id":"guard","type":"package","package":"nftables"}]}`
var m Manifest
if err := json.Unmarshal([]byte(raw), &m); err != nil {
t.Fatal(err)
@@ -62,6 +64,8 @@ func TestWhatAMoveUnderRecordHoldsFor(t *testing.T) {
{"a provider", func(m *Manifest) { m.Requires = append(m.Requires, "s3") }, "what it requires"},
{"what it provides", func(m *Manifest) { m.Provides = append(m.Provides, Offer{Name: "smtp"}) }, "what it provides"},
{"a process of a module with data", func(m *Manifest) { resource(m, "watcher")["command"] = "watch2" }, "long-running process watcher"},
{"an archive resource", func(m *Manifest) { resource(m, "installation")["artifact"] = "archive2" }, "the archive installation"},
{"a package resource", func(m *Manifest) { resource(m, "guard")["package"] = "nftables2" }, "the package guard"},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
+6
View File
@@ -195,6 +195,12 @@ func Holds(from, to Manifest) string {
if keepsData {
return fmt.Sprintf("it changes its long-running %s %s, and it keeps data of its own", kind, id)
}
default:
// Anything that is not a file or directory (judged above by whether a container mounts it
// or it holds data) always holds: an archive, a package, a user, a network, or any type
// added later (novox/hq ADR 0377 §3's allowlist names only tools bundles and manifest
// fields that recreate nothing).
return fmt.Sprintf("it changes the %s %s", kind, id)
}
}
}