Issue 1585, S19: the strict parser, every manifest problem function and the judge's tables (192 declarations, about 5,200 lines) and their 244 tests leave; composition keeps its own refusals and the helpers it uses. The catalogue's tests decode the manifests they build. ParseManifest, ParseManifestWith, ParseManifestForInputs, CatalogueProblems and the rest are gone; mesh-delivery-indexer/judge holds them. Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
74 lines
3.1 KiB
Go
74 lines
3.1 KiB
Go
package catalogue
|
|
|
|
import (
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// A bind mount the module never declared is refused where it is written (novox/hq 04-ISSUES/026,
|
|
// ADR 0091). The container runtime creates a missing bind source itself, as root, with a mode it
|
|
// picks, so the module's own owner and mode never reach the directory holding its data, and the
|
|
// rule that keeps data when a module goes away (ADR 0030) does not cover it.
|
|
|
|
const aContainerMounting = `{"id":"server","type":"container","name":"store","image":"x@sha256:` +
|
|
`0000000000000000000000000000000000000000000000000000000000000000","volumes":["%s:/inside"]}`
|
|
|
|
func manifestMounting(from string, beside string) []byte {
|
|
res := aContainerMounting
|
|
if beside != "" {
|
|
res = beside + "," + res
|
|
}
|
|
return []byte(`{"module":"store","resources":[` + strings.Replace(res, "%s", from, 1) + `]}`)
|
|
}
|
|
|
|
// Declaring it is enough — including declaring the directory above it.
|
|
func TestAMountUnderADeclaredDirectoryIsAccepted(t *testing.T) {
|
|
_, err := decodeForTest(manifestMounting("/services/store/data",
|
|
`{"id":"state","type":"directory","path":"/services/store","mode":"0700"}`))
|
|
if err != nil {
|
|
t.Fatalf("a module that said where its data lives was refused anyway: %v", err)
|
|
}
|
|
}
|
|
|
|
// The operator's path, granted for use (ADR 0051), is declared by `accesses`, not by a directory the
|
|
// module would then own.
|
|
func TestAMountOfAnAccessedPathIsAccepted(t *testing.T) {
|
|
_, err := decodeForTest([]byte(`{"module":"store","accesses":[{"path":"/services/media/movies","mode":"read"}],` +
|
|
`"resources":[` + strings.Replace(aContainerMounting, "%s", "/services/media/movies", 1) + `]}`))
|
|
if err != nil {
|
|
t.Fatalf("a mount of a path the module declares it accesses was refused: %v", err)
|
|
}
|
|
}
|
|
|
|
// **Every manifest in the catalogue (internal/beside) passes**, so the rule is not one the catalogue
|
|
// is already breaking.
|
|
func TestEveryCatalogueManifestDeclaresWhatItMounts(t *testing.T) {
|
|
_, files := catalogueManifests(t)
|
|
for _, f := range files {
|
|
if f.err != nil {
|
|
t.Errorf("%s: %v", filepath.Base(filepath.Dir(f.path)), f.err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Where a bound fact lands is the mesh's file too, and a container may mount it directly.
|
|
func TestAMountOfABoundFactIsAccepted(t *testing.T) {
|
|
_, err := decodeForTest([]byte(`{"module":"store","requires":["model-access"],` +
|
|
`"binds":{"model-access":"/var/lib/store/model.json"},"resources":[` +
|
|
strings.Replace(aContainerMounting, "%s", "/var/lib/store/model.json", 1) + `]}`))
|
|
if err != nil {
|
|
t.Fatalf("a mount of the file the mesh writes a binding to was refused: %v", err)
|
|
}
|
|
}
|
|
|
|
// The file a filter module's rule set is written to is declared by `filtering.into` (novox/hq ADR
|
|
// 0169): the module's runtime mounts it to reload the mesh's own table, and nothing else declares it.
|
|
func TestAMountOfTheFilterFileIsDeclaredByFilteringInto(t *testing.T) {
|
|
_, err := decodeForTest([]byte(`{"module":"nftables","filtering":{"into":"/etc/nftables.conf"},` +
|
|
`"resources":[` + strings.Replace(aContainerMounting, "%s", "/etc/nftables.conf", 1) + `]}`))
|
|
if err != nil {
|
|
t.Fatalf("a filter module mounting its own filter file was refused: %v", err)
|
|
}
|
|
}
|