package bootstrap import ( "context" "encoding/json" "errors" "strings" "testing" "github.com/novox/mesh-host/internal/declaration" ) // Defends novox/hq ADR 0338 (issue 609): genesis raises its store as the postgres module's server, so // the store step adopts it as it runs. Until then the bundle raised `mesh-store`, on another image, in a // docker volume, and genesis stopped at step 17 of 24. // theStoreGenesisRaises is the store container of the bundle as the installer applies it: rewritten for // this machine's credentials, so its password is the file the module reads too. func theStoreGenesisRaises(t *testing.T) *declaration.Container { t.Helper() r := producedBundle(t) store, err := storeIn(r.Declaration) if err != nil { t.Fatal(err) } return store } // The bundle's store is exactly the catalogue's postgres module's server, read from the module's own // manifest (ADR 0338 §1): its name, its image (so a drift of either pin fails here), where its data is, // and the file its superuser password is read from, with the environment that says so. func TestTheBundleRaisesTheStoreAsTheCataloguesPostgresModuleDeclaresIt(t *testing.T) { manifest := theCataloguesModule(t, "postgres") var m struct { Resources []struct { ID string `json:"id"` Type string `json:"type"` Name string `json:"name"` Image string `json:"image"` Env map[string]string `json:"env"` Ports []string `json:"ports"` Volumes []string `json:"volumes"` } `json:"resources"` } if err := json.Unmarshal(manifest, &m); err != nil { t.Fatal(err) } ours := theStoreGenesisRaises(t) found := false for _, r := range m.Resources { if r.Type != "container" || r.ID != "server" { continue } found = true if ours.Name != r.Name { t.Errorf("the bundle raises the store as %q; the module's server is %q", ours.Name, r.Name) } if ours.Image != r.Image { t.Errorf("the bundle raises the store on %s; the module pins %s — the two pins move together", ours.Image, r.Image) } for _, key := range []string{"POSTGRES_PASSWORD_FILE", "PGDATA"} { if ours.Env[key] != r.Env[key] { t.Errorf("the bundle's store has %s=%q; the module's %q", key, ours.Env[key], r.Env[key]) } } if _, has := ours.Env["POSTGRES_PASSWORD"]; has { t.Error("the bundle's store takes its password from its environment; the module reads a file") } // The module's own directory for its state is /, where genesis keeps the // superuser's file (StoreSuperuserFile). var theirs []string for _, v := range r.Volumes { theirs = append(theirs, strings.ReplaceAll(v, "${dir:state}", "/var/lib/postgres")) } if strings.Join(ours.Volumes, " ") != strings.Join(theirs, " ") { t.Errorf("the bundle's store mounts %v; the module's %v", ours.Volumes, theirs) } if strings.Join(ours.Ports, " ") != strings.Join(r.Ports, " ") { t.Errorf("the bundle publishes the store on %v; the module on %v", ours.Ports, r.Ports) } // And the store step's own check agrees: by name and image, before anything is registered. if err := serverMatchesFoundation(manifest, ours, "postgres"); err != nil { t.Errorf("the store step would refuse the store the bundle raises: %v", err) } } if !found { t.Fatal("the postgres module declares no server container") } } // The store's data directory is made by the bundle before the store starts, owned by the image's // postgres user, as the module declares it (ADR 0338 §1, and the measurement under its §5). func TestTheBundleMakesTheStoresDataDirectoryAsTheModuleDoes(t *testing.T) { r := producedBundle(t) at, store := -1, -1 for i, res := range r.Declaration.Resources { if d, ok := res.(*declaration.Directory); ok && d.Path == StoreDataDir { at = i if d.Mode != "0700" || d.Owner != "999:70" { t.Errorf("the store's data directory is made %s %s; the module makes it 0700 999:70", d.Mode, d.Owner) } } if res.Identity() == StoreID { store = i } } if at < 0 || store < 0 || at > store { t.Fatalf("the store's data directory %s is not made before the store (directory at %d, store at %d)", StoreDataDir, at, store) } } // No step of the bundle and no step of the installer names `mesh-store`, which is the seat's name only // (ADR 0338 §2): every step that reaches the store reaches the container the bundle raised. func TestNoStepReachesTheStoreByTheSeatsName(t *testing.T) { text := string(theRealBundle(t)) for _, by := range []string{`"in": "mesh-store"`, `container:mesh-store`, `exec mesh-store`, `"name": "mesh-store"`, `mesh-store-data`} { if strings.Contains(text, by) { t.Errorf("the bundle still reaches the store as %s", by) } } store := theStoreGenesisRaises(t) if foundationStore != store.Name { t.Errorf("the packages step reaches the store as %q; the bundle raises it as %q", foundationStore, store.Name) } r := producedBundle(t) for _, res := range r.Declaration.Resources { if a, ok := res.(*declaration.Action); ok && a.In != "" && a.In != store.Name && strings.Contains(a.ID, "database") { t.Errorf("the step %s reaches the store in %q", a.ID, a.In) } } } // The installer's refusal to raise a store over data it cannot open looks where the store keeps its data // (ADR 0338 §2): a cluster there and no credential is refused; the old docker volume alone is not. func TestTheRefusalOverExistingDataLooksInTheStoresDirectory(t *testing.T) { made := RootCredentials{Store: "s", Bus: "b", StoreMade: true} machine := func(cluster, volume bool) Runner { return func(_ context.Context, name string, args ...string) (string, error) { joined := name + " " + strings.Join(args, " ") switch { case joined == "test -e "+StoreDataDir+"/pgdata/PG_VERSION": if cluster { return "", nil } case strings.HasPrefix(joined, "docker volume inspect"): if volume { return "[]", nil } } return "", errors.New("exit status 1") } } err := RefuseExistingServers(context.Background(), machine(true, false), made) if err == nil || !strings.Contains(err.Error(), StoreDataDir) { t.Fatalf("a cluster in %s and no credential was not refused naming it: %v", StoreDataDir, err) } if err := RefuseExistingServers(context.Background(), machine(false, true), made); err != nil { t.Fatalf("a machine holding only the old volume was refused: %v", err) } if err := RefuseExistingServers(context.Background(), machine(true, false), RootCredentials{Store: "s"}); err != nil { t.Fatalf("a kept credential was refused: %v", err) } }