mesh/delivery merged: source.main moved to novox/mesh-host@998d8f737008
mesh/check being carried to 5dc3a8e17f97 on 998d8f737008 | from fabfdf04f78a
The journal of an unpack sat at <target>.unpacked.json, in a directory the module's account may write: a forged one would make the root engine remove or adopt any path it named. It now lives under <statedir>/unpacked/<sha256 of the target>.json, mode 0600, names its target, and is refused whole when an entry is not filepath.IsLocal or a parent is not above the target. It goes when the archive is taken off, whether its state record still said what it unpacked or not. The launcher's stop grace for a host on trial rises from 20 s to 75 s and the unit's TimeoutStopSec is 120 s (openrc the same), past the host's own 60 s wait for the apply in flight, so neither cuts a save off again. Tests folded as rows into existing ones (the repository is over its unit budget): the journal's rows, a forged entry and parent refused, the journal gone with a record that lost its Unpacked; the test that passed on main dropped; the stop test waits on its own line, not a sleep, and no longer consumes the save it waits for. Claude-Session: https://claude.ai/code/session_01JG73KG7AfSdh6FP8VrBqCn
69 lines
2.8 KiB
Go
69 lines
2.8 KiB
Go
package apply
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/novox/mesh-host/internal/store"
|
|
)
|
|
|
|
// novox/hq issue 220: the tree on disk is exactly the archive. A file the previous archive had and
|
|
// this one does not is gone, and nothing is left beside the directory.
|
|
func TestAnArchiveReplacesTheTreeItWasUnpackedOver(t *testing.T) {
|
|
dir := t.TempDir()
|
|
first, firstDigest := anArchive(t, map[string]string{"index.js": "old", "node_modules/dep/index.js": "dep"})
|
|
d := declare(t, `{"id":"bundle-tools","type":"archive","source":"`+serving(t, first)+
|
|
`","digest":"`+firstDigest+`","path":"`+dir+`/tools"}`)
|
|
_, state, err := Apply(context.Background(), archHost(t), d, store.State{}, store.OriginCarried, noServices, nil, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
second, secondDigest := anArchive(t, map[string]string{"index.js": "one file"})
|
|
d = declare(t, `{"id":"bundle-tools","type":"archive","source":"`+serving(t, second)+
|
|
`","digest":"`+secondDigest+`","path":"`+dir+`/tools"}`)
|
|
if _, _, err := Apply(context.Background(), archHost(t), d, state, store.OriginCarried, noServices, nil, nil); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got, _ := os.ReadFile(dir + "/tools/index.js"); string(got) != "one file" {
|
|
t.Fatalf("index.js is %q", got)
|
|
}
|
|
if _, err := os.Stat(dir + "/tools/node_modules"); err == nil {
|
|
t.Fatal("the previous archive's directory is still there")
|
|
}
|
|
entries, _ := os.ReadDir(dir)
|
|
if len(entries) != 1 {
|
|
names := []string{}
|
|
for _, e := range entries {
|
|
names = append(names, e.Name())
|
|
}
|
|
t.Fatalf("beside the tree: %v", names)
|
|
}
|
|
}
|
|
|
|
// A tree is replaced only by one that unpacked whole: an archive refused halfway leaves the old
|
|
// tree as it was.
|
|
func TestARefusedArchiveLeavesTheTreeItWouldHaveReplaced(t *testing.T) {
|
|
dir := t.TempDir()
|
|
first, firstDigest := anArchive(t, map[string]string{"index.js": "old"})
|
|
d := declare(t, `{"id":"bundle-tools","type":"archive","source":"`+serving(t, first)+
|
|
`","digest":"`+firstDigest+`","path":"`+dir+`/tools"}`)
|
|
_, state, err := Apply(context.Background(), archHost(t), d, store.State{}, store.OriginCarried, noServices, nil, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
bad, badDigest := anArchive(t, map[string]string{"index.js": "new", "../../escaped": "no"})
|
|
d = declare(t, `{"id":"bundle-tools","type":"archive","source":"`+serving(t, bad)+
|
|
`","digest":"`+badDigest+`","path":"`+dir+`/tools"}`)
|
|
if _, _, err := Apply(context.Background(), archHost(t), d, state, store.OriginCarried, noServices, nil, nil); err == nil {
|
|
t.Fatal("an escaping archive was accepted")
|
|
}
|
|
if got, _ := os.ReadFile(dir + "/tools/index.js"); string(got) != "old" {
|
|
t.Fatalf("index.js is %q after a refused archive", got)
|
|
}
|
|
if entries, _ := os.ReadDir(dir); len(entries) != 1 {
|
|
t.Fatalf("%d entries beside the tree after a refused archive", len(entries))
|
|
}
|
|
}
|