package layout import ( "path/filepath" "strings" "testing" "github.com/carlosframework/platform/internal/provision" ) func TestPlanForNestedDerivesEveryPathFromTheHost(t *testing.T) { p, err := PlanFor("/data", "/run/carlos", "h.example.com", Mode("nested")) if err != nil { t.Fatal(err) } iid := provision.InstanceID("h.example.com") if p.From != (Endpoint{DB: "/data/h.example.com.db", Socket: "/run/carlos/h.example.com.sock"}) { t.Errorf("from = %+v", p.From) } if p.To != (Endpoint{DB: provision.NestedDBPath("/data", "h.example.com"), Socket: provision.NestedSocket("h.example.com")}) { t.Errorf("to = %+v", p.To) } if p.Host != "h.example.com" || p.IID != iid || p.Mode != Mode("nested") { t.Errorf("host/iid/mode = %q %q %q", p.Host, p.IID, p.Mode) } if len(p.Artifacts) != 9 { t.Fatalf("len(Artifacts) = %d, want 9", len(p.Artifacts)) } byName := map[string]Artifact{} for _, a := range p.Artifacts { byName[a.Name] = a } if byName["db"].Src != "/data/h.example.com.db" || byName["db"].Dst != filepath.Join("/data/.instances", iid, "h.example.com.db") || !byName["db"].Required { t.Errorf("db artifact = %+v (the destination must be the RAW directory)", byName["db"]) } if byName["db"].Dir { t.Errorf("db artifact should not be marked as a directory: %+v", byName["db"]) } if byName["env"].Dst != filepath.Join("/data/.instances", iid, "h.example.com.env") || byName["env"].Required { t.Errorf("env artifact = %+v", byName["env"]) } if byName["db-wal"].Src != "/data/h.example.com.db-wal" || byName["db-shm"].Src != "/data/h.example.com.db-shm" { t.Errorf("sidecar files = %+v %+v", byName["db-wal"], byName["db-shm"]) } if byName["config-env"].Src != "/data/h.example.com.config.env" || byName["sidecar-env"].Src != "/data/h.example.com-sidecar.env" { t.Errorf("env files = %+v %+v", byName["config-env"], byName["sidecar-env"]) } if strings.Contains(byName["db"].Dst, "/data/instances/") { t.Error("a destination through the bind mount would EXDEV") } // The two artifacts appended after socket. if byName["litestream-shadow"].Src != "/data/.activator-h.example.com.db-litestream" { t.Errorf("litestream-shadow src = %q", byName["litestream-shadow"].Src) } if byName["litestream-shadow"].Dst != filepath.Join("/data/.instances", iid, ".activator-h.example.com.db-litestream") { t.Errorf("litestream-shadow dst = %q", byName["litestream-shadow"].Dst) } if !byName["litestream-shadow"].Dir { t.Error("litestream-shadow should be marked as a directory") } if byName["litestream-shadow"].Required { t.Error("litestream-shadow must be optional") } if byName["corrupt"].Src != "/data/h.example.com.db.corrupt" { t.Errorf("corrupt src = %q", byName["corrupt"].Src) } if byName["corrupt"].Dst != filepath.Join("/data/.instances", iid, "h.example.com.db.corrupt") { t.Errorf("corrupt dst = %q", byName["corrupt"].Dst) } if byName["corrupt"].Dir || byName["corrupt"].Required { t.Errorf("corrupt artifact = %+v, want a non-directory optional file", byName["corrupt"]) } // Both socket endpoints come from the socketDir argument, and the // nested one sits under "instances" (the mount-visible name), not the // raw dotted directory: nothing is ever renamed for the socket. if byName["socket"].Src != "/run/carlos/h.example.com.sock" { t.Errorf("socket src = %q", byName["socket"].Src) } if byName["socket"].Dst != filepath.Join("/run/carlos/instances", iid, "app.sock") { t.Errorf("socket dst = %q", byName["socket"].Dst) } if byName["socket"].Required { t.Error("socket must be optional: it is reported, never renamed") } } // TestPlanForUsesTheGivenSocketDirNotTheCompiledDefault pins that PlanFor // must derive both socket endpoints from its socketDir argument, never from // the compiled-in provision.SocketDir or provision.DefaultSocket/NestedSocket // (which ignore the argument entirely). A test that only ever passes // "/run/carlos" as socketDir cannot catch a PlanFor that silently falls back // to the compiled default, because the two values coincide in production. func TestPlanForUsesTheGivenSocketDirNotTheCompiledDefault(t *testing.T) { sd := "/tmp/not-run-carlos" p, err := PlanFor("/data", sd, "h.example.com", Mode("nested")) if err != nil { t.Fatal(err) } iid := provision.InstanceID("h.example.com") if p.From.Socket != filepath.Join(sd, "h.example.com.sock") { t.Errorf("from.socket = %q, want under %q", p.From.Socket, sd) } if p.To.Socket != filepath.Join(sd, "instances", iid, "app.sock") { t.Errorf("to.socket = %q, want under %q", p.To.Socket, sd) } if strings.HasPrefix(p.To.Socket, "/run/carlos") { t.Error("nested socket endpoint fell back to the compiled-in socket dir") } for _, a := range p.Artifacts { if a.Name == "socket" && strings.HasPrefix(a.Dst, "/run/carlos") { t.Errorf("socket artifact fell back to the compiled-in socket dir: %+v", a) } } } func TestPlanForFlatIsTheExactReverse(t *testing.T) { n, _ := PlanFor("/data", "/run/carlos", "h.example.com", Mode("nested")) f, err := PlanFor("/data", "/run/carlos", "h.example.com", Mode("flat")) if err != nil { t.Fatal(err) } if f.From != n.To || f.To != n.From { t.Errorf("flat endpoints %+v %+v are not nested's swapped %+v %+v", f.From, f.To, n.From, n.To) } if len(f.Artifacts) != len(n.Artifacts) { t.Fatalf("artifact counts differ: %d vs %d", len(f.Artifacts), len(n.Artifacts)) } for i := range n.Artifacts { if f.Artifacts[i].Src != n.Artifacts[i].Dst || f.Artifacts[i].Dst != n.Artifacts[i].Src { t.Errorf("artifact %s not reversed: %+v vs %+v", n.Artifacts[i].Name, f.Artifacts[i], n.Artifacts[i]) } if f.Artifacts[i].Name != n.Artifacts[i].Name || f.Artifacts[i].Required != n.Artifacts[i].Required || f.Artifacts[i].Dir != n.Artifacts[i].Dir { t.Errorf("artifact %d metadata not preserved across the reversal: %+v vs %+v", i, f.Artifacts[i], n.Artifacts[i]) } } } // TestRawRunDirIsTheDottedRawSocketTree pins rawRunDir's own doc comment // ("/.instances/"): a later pass creates the nested run // directory through this raw path even though the socket file inside it is // never renamed (PlanFor's socket artifact records the mount-visible path // instead). func TestRawRunDirIsTheDottedRawSocketTree(t *testing.T) { got := rawRunDir("/run/carlos", "h.example.com") want := filepath.Join("/run/carlos/.instances", provision.InstanceID("h.example.com")) if got != want { t.Errorf("rawRunDir = %q, want %q", got, want) } } // NestedPlatformNames is exactly the plan's raw-directory names (every // nested artifact the pass moves into the instance directory, the socket // aside) plus the generation markers, litestream's stock meta directory and // the adoption record; and it covers every name a nested plan's artifacts // land at in the raw directory, so no platform file of the layout pass can // reach an app archive. func TestNestedPlatformNamesAreThePlansRawNames(t *testing.T) { const h = "h.example.com" got := NestedPlatformNames(h) want := map[string]bool{ h + ".db": true, h + ".db-wal": true, h + ".db-shm": true, h + ".db.corrupt": true, h + ".env": true, h + "-sidecar.env": true, h + ".config.env": true, ".activator-" + h + ".db-litestream": true, ".files-gen": true, h + ".db.gen": true, h + ".db-litestream": true, h + ".instance-key-adoption.json": true, h + ".db.seed": true, "launch.run.json": true, "launch.seed.json": true, "launch.sidecar.json": true, "launch.cmd.json": true, } if len(got) != len(want) { t.Fatalf("NestedPlatformNames = %v, want %v", got, want) } for n := range want { if !got[n] { t.Errorf("%s missing", n) } } p, err := PlanFor("/data", "/run/carlos", h, ModeNested) if err != nil { t.Fatal(err) } raw := rawInstanceDir("/data", h) for _, a := range p.Artifacts { if filepath.Dir(a.Dst) == raw && !got[filepath.Base(a.Dst)] { t.Errorf("artifact %s lands at %s, which NestedPlatformNames does not name", a.Name, a.Dst) } if a.Name == "socket" && got[filepath.Base(a.Dst)] { t.Errorf("the socket is in the run directory, not the instance directory") } } } // IsNestedPlatformName is NestedPlatformNames plus a generation's launch // descriptor exactly as instancectl.GenLaunchFileName writes it; anything // shaped almost like one is the app's. func TestIsNestedPlatformNameMatchesGenerationLaunchFilesExactly(t *testing.T) { const h = "h.example.com" for name := range NestedPlatformNames(h) { if !IsNestedPlatformName(h, name) { t.Errorf("%s: not a platform name", name) } } for _, name := range []string{"launch.gen.0.json", "launch.gen.7.json", "launch.gen.18446744073709551615.json"} { if !IsNestedPlatformName(h, name) { t.Errorf("%s: not a platform name", name) } } for _, name := range []string{h, "launch.gen.json", "launch.gen..json", "launch.gen.07.json", "launch.gen.-1.json", "launch.gen.7.json.bak", "launch.gen.7", "xlaunch.gen.7.json", "launch.gen.x.json", "launch.gen.18446744073709551616.json", "launch.other.json", h + "-blobs", "uploads"} { if IsNestedPlatformName(h, name) { t.Errorf("%s: taken for a platform name", name) } } } func TestPlanForRefusesBadInput(t *testing.T) { if _, err := PlanFor("/data", "/run/carlos", "h.example.com", Mode("sideways")); err == nil { t.Error("unknown mode accepted") } if _, err := PlanFor("/data", "/run/carlos", "../h", Mode("nested")); err == nil { t.Error("invalid host accepted") } }