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) } }