2da23462c9
The deploy-side double-nest fix lives in the app catalog (templates dropped the extra felhom-data segment). This adds the controller-side invariant test that ties the deploy path (ParseComposeHDDMounts) to the backup path (AppDataDir/NamespaceRoot) so they can't drift again, plus the v0.52.0 CHANGELOG. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
70 lines
3.0 KiB
Go
70 lines
3.0 KiB
Go
package stacks_test
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"gitea.dooplex.hu/admin/felhom-controller/internal/appbackup"
|
|
"gitea.dooplex.hu/admin/felhom-controller/internal/stacks"
|
|
)
|
|
|
|
// TestDeployPathAgreesWithBackupHelpers is the Phase-1 GATE invariant: the host path an app actually
|
|
// writes to — resolved from its compose ${HDD_PATH} bind mounts, exactly as deploy does — must be
|
|
// byte-identical to where the app-data backup helpers look. Single-nested, no doubled felhom-data.
|
|
//
|
|
// Before the fix, catalog templates used ${HDD_PATH}/felhom-data/appdata/<app>. On a Model-A in-guest
|
|
// drive the guest mount /mnt/<drive> ALREADY is the host's <drive>/felhom-data namespace, so that extra
|
|
// segment produced .../felhom-data/felhom-data/... on disk, while NamespaceRoot(drive, true) resolved
|
|
// the single-nested .../appdata/<app>. Deploy and backup disagreed. The catalog templates now use
|
|
// ${HDD_PATH}/appdata/<app>; this test locks the two sides together so they can't drift again.
|
|
func TestDeployPathAgreesWithBackupHelpers(t *testing.T) {
|
|
const hddPath = "/mnt/felhom-usb" // an enrolled in-guest drive mount (basename is NOT felhom-data)
|
|
const app = "romm"
|
|
|
|
// A compose volumes block in the FIXED form (no felhom-data segment), mirroring the catalog template.
|
|
compose := "services:\n" +
|
|
" romm:\n" +
|
|
" volumes:\n" +
|
|
" - ${HDD_PATH}/appdata/romm/library:/romm/library\n" +
|
|
" - ${HDD_PATH}/appdata/romm/resources:/romm/resources\n" +
|
|
" - romm_config:/romm/config\n" +
|
|
"volumes:\n" +
|
|
" romm_config:\n"
|
|
|
|
dir := t.TempDir()
|
|
composePath := filepath.Join(dir, "docker-compose.yml")
|
|
if err := os.WriteFile(composePath, []byte(compose), 0600); err != nil {
|
|
t.Fatalf("writing temp compose: %v", err)
|
|
}
|
|
|
|
// Deploy side: where the app's bytes actually land (named volume romm_config is correctly excluded).
|
|
mounts := stacks.ParseComposeHDDMounts(composePath, hddPath)
|
|
if len(mounts) != 2 {
|
|
t.Fatalf("expected 2 HDD bind mounts, got %d: %v", len(mounts), mounts)
|
|
}
|
|
|
|
// Backup side: the app-data dir the helpers resolve for the same in-guest drive.
|
|
ns := appbackup.NamespaceRoot(hddPath, true) // in-guest Model-A drive → mount as-is, no felhom-data
|
|
wantAppDir := filepath.ToSlash(appbackup.AppDataDir(ns, app))
|
|
if wantAppDir != "/mnt/felhom-usb/appdata/romm" {
|
|
t.Fatalf("backup app-data dir unexpected: %q", wantAppDir)
|
|
}
|
|
|
|
for _, m := range mounts {
|
|
s := filepath.ToSlash(m)
|
|
// (1) No doubled felhom-data, and in guest-path space no felhom-data segment at all.
|
|
if strings.Contains(s, appbackup.FelhomDataDir+"/"+appbackup.FelhomDataDir) {
|
|
t.Errorf("deploy mount double-nests felhom-data: %q", s)
|
|
}
|
|
if n := strings.Count(s, appbackup.FelhomDataDir); n > 0 {
|
|
t.Errorf("deploy mount unexpectedly carries %d felhom-data segment(s): %q", n, s)
|
|
}
|
|
// (2) Every app bind mount lives under the backup helpers' app-data dir — deploy and backup agree.
|
|
if !strings.HasPrefix(s, wantAppDir+"/") {
|
|
t.Errorf("deploy mount %q is not under backup app-data dir %q — the two sides diverge", s, wantAppDir)
|
|
}
|
|
}
|
|
}
|