v0.172.0 fixup 2: drop import/* from the carry-list
Found on the demo-hp live leg: with import, import/paperless and import/calibre in the carry-list, the derived skeleton RE-CREATES a per-drive drop-zone on every drive forever — the dead lookalike the canonical root exists to remove, and one that is never backed up (class: excluded). Not a zero-removals violation: nothing deletes what an existing box has. Both demo boxes' old drop-zones were verified to hold zero files before the change.
This commit is contained in:
+11
-2
@@ -75,12 +75,21 @@ picker paths, and the system drive is deliberately not a registered StoragePath.
|
|||||||
`{{if .System}}` would have failed at render for every share. `ShareRow` is now package-level and the
|
`{{if .System}}` would have failed at render for every share. `ShareRow` is now package-level and the
|
||||||
render test constructs the exact type the handler passes.
|
render test constructs the exact type the handler passes.
|
||||||
|
|
||||||
**Caught during the live deploy leg and fixed in the same version:** `EnsureImportRoot` ensured only
|
**Caught during the live legs and fixed in the same version (two things):**
|
||||||
|
|
||||||
|
1. The carry-list initially kept `import`, `import/paperless` and `import/calibre`, so the skeleton
|
||||||
|
would RE-CREATE a per-drive drop-zone on every drive forever — the exact dead lookalike this arc
|
||||||
|
removes, and one that is never backed up. Dropped from the carry-list. This is not a removal:
|
||||||
|
nothing deletes the dirs an existing box has (both demo boxes' old drop-zones were verified to hold
|
||||||
|
**zero files** first); they stop being maintained and stop appearing on fresh boxes.
|
||||||
|
`TestSkeletonNeverCreatesAPerDriveDropZone` pins it, and `TestUserdataSkeleton_List` was updated to
|
||||||
|
assert their absence.
|
||||||
|
2. `EnsureImportRoot` ensured only
|
||||||
the leaf, so `MkdirAll`'s intermediates left `<sysNS>/userdata` at `755 root:root` — the one userdata
|
the leaf, so `MkdirAll`'s intermediates left `<sysNS>/userdata` at `755 root:root` — the one userdata
|
||||||
root on the box outside the 2775/gid-1000 convention. Both the parent and the import dir now carry it
|
root on the box outside the 2775/gid-1000 convention. Both the parent and the import dir now carry it
|
||||||
(`TestEnsureImportRoot_ParentCarriesTheConvention`).
|
(`TestEnsureImportRoot_ParentCarriesTheConvention`).
|
||||||
|
|
||||||
Tests 915 → 950, all green. Red-proofs recorded in REPORT.md for Scenario B (classification),
|
Tests 915 → 951, all green. Red-proofs recorded in REPORT.md for Scenario B (classification),
|
||||||
C (determinism) and E (server-side share refusal). No destructive filesystem call was added anywhere
|
C (determinism) and E (server-side share refusal). No destructive filesystem call was added anywhere
|
||||||
in this arc. **MinAgent unchanged.**
|
in this arc. **MinAgent unchanged.**
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"slices"
|
"slices"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -132,3 +133,31 @@ func TestEnsureUserdataSkeletonCreatesOnly(t *testing.T) {
|
|||||||
t.Errorf("a pre-existing customer dir was removed — zero-removals violated: %v", err)
|
t.Errorf("a pre-existing customer dir was removed — zero-removals violated: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// R-75: a DATA drive must never get a per-drive drop-zone from the skeleton. Carrying the old
|
||||||
|
// `import/*` entries would have the skeleton re-create a dead lookalike on every drive forever —
|
||||||
|
// one that is also never backed up, since import paths are class: excluded.
|
||||||
|
//
|
||||||
|
// This is NOT a zero-removals violation: nothing deletes the dirs a box already has (see
|
||||||
|
// TestEnsureUserdataSkeletonCreatesOnly). They stop being maintained and stop appearing on fresh boxes.
|
||||||
|
func TestSkeletonNeverCreatesAPerDriveDropZone(t *testing.T) {
|
||||||
|
// The catalog no longer implies any ${USERDATA_PATH}/import path — the binds moved to
|
||||||
|
// ${IMPORT_PATH} — so the only way one could appear is via the carry-list.
|
||||||
|
for _, derived := range [][]string{nil, {"media/podcasts", "roms"}} {
|
||||||
|
for _, d := range BuildUserdataSkeleton(derived) {
|
||||||
|
if d == "import" || strings.HasPrefix(d, "import/") {
|
||||||
|
t.Errorf("derived=%v: skeleton created a per-drive drop-zone %q — the canonical root is on the SYSTEM drive", derived, d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, c := range UserdataSkeletonCarry() {
|
||||||
|
if c == "import" || strings.HasPrefix(c, "import/") {
|
||||||
|
t.Errorf("the carry-list still holds %q", c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// A catalog app that genuinely declares a ${USERDATA_PATH}/import/... bind would still be
|
||||||
|
// honoured — the rule is "don't carry them", not "filter them out".
|
||||||
|
if !slices.Contains(BuildUserdataSkeleton([]string{"import/valami"}), "import/valami") {
|
||||||
|
t.Error("a genuinely derived userdata import path must still be created")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -57,20 +57,25 @@ func ImportDir(nsRoot string) string {
|
|||||||
//
|
//
|
||||||
// - `documents` is implied by NO catalog app (SPIKE P0(a)) yet exists on both demo boxes and is
|
// - `documents` is implied by NO catalog app (SPIKE P0(a)) yet exists on both demo boxes and is
|
||||||
// customer-visible — it may hold customer files. Derivation alone would drop it.
|
// customer-visible — it may hold customer files. Derivation alone would drop it.
|
||||||
// - `import/paperless` and `import/calibre` moved to the canonical system-drive root in R-75, so
|
|
||||||
// derivation no longer implies them under a data drive either. The pre-existing ones stay put;
|
|
||||||
// nothing in this arc deletes a directory.
|
|
||||||
//
|
//
|
||||||
// It doubles as the fresh-box floor: on a box whose catalog has not synced yet the derived set is
|
// It doubles as the fresh-box floor: on a box whose catalog has not synced yet the derived set is
|
||||||
// empty, and the customer still gets the full standard tree instead of a nearly-empty one.
|
// empty, and the customer still gets the full standard tree instead of a nearly-empty one.
|
||||||
//
|
//
|
||||||
|
// DELIBERATELY ABSENT: `import`, `import/paperless`, `import/calibre`. They were in the v0.171.0
|
||||||
|
// hardcoded list, and carrying them would have the skeleton RE-CREATE a per-drive drop-zone on every
|
||||||
|
// drive forever — the exact dead-lookalike R-75 exists to remove, and one that is never backed up
|
||||||
|
// (`class: excluded`). Zero-removals is about not DELETING what a box already has, not about
|
||||||
|
// re-creating it on boxes that never had it: nothing here removes the pre-existing dirs on
|
||||||
|
// demo-felhom / demo-hp, they simply stop being maintained and stop appearing on fresh boxes.
|
||||||
|
// Verified before the change: both boxes' old drop-zones held ZERO files (2026-07-26). A box with
|
||||||
|
// pending files in an old drop-zone would need an operator-run move — see REPORT.md.
|
||||||
|
//
|
||||||
// ASCII, no spaces (flows through ${} interpolation, shell, and the rsync merge walk).
|
// ASCII, no spaces (flows through ${} interpolation, shell, and the rsync merge walk).
|
||||||
func UserdataSkeletonCarry() []string {
|
func UserdataSkeletonCarry() []string {
|
||||||
return []string{
|
return []string{
|
||||||
"media", "media/movies", "media/tv", "media/music", "media/audiobooks",
|
"media", "media/movies", "media/tv", "media/music", "media/audiobooks",
|
||||||
"media/books", "media/comics", "media/photos",
|
"media/books", "media/comics", "media/photos",
|
||||||
"downloads",
|
"downloads",
|
||||||
"import", "import/paperless", "import/calibre",
|
|
||||||
"roms",
|
"roms",
|
||||||
"documents",
|
"documents",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,9 +13,12 @@ func TestSharedContentGID(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestUserdataSkeleton_List asserts the locked skeleton subdir set. R-75 renamed the hardcoded list
|
// TestUserdataSkeleton_List asserts the locked carry-list. R-75 renamed the hardcoded list to
|
||||||
// to UserdataSkeletonCarry (it is now the non-derived carry-list); the asserted set is UNCHANGED,
|
// UserdataSkeletonCarry (it is now the non-derived carry-list) and DELIBERATELY dropped the three
|
||||||
// which is exactly the zero-removals promise.
|
// `import*` entries: carrying them would re-create a per-drive drop-zone on every drive forever, the
|
||||||
|
// dead lookalike the canonical root exists to remove. That is not a removal — nothing deletes the
|
||||||
|
// dirs an existing box has; they stop being maintained and stop appearing on fresh boxes. Every other
|
||||||
|
// entry is unchanged, which is the zero-removals promise.
|
||||||
func TestUserdataSkeleton_List(t *testing.T) {
|
func TestUserdataSkeleton_List(t *testing.T) {
|
||||||
got := map[string]bool{}
|
got := map[string]bool{}
|
||||||
for _, s := range UserdataSkeletonCarry() {
|
for _, s := range UserdataSkeletonCarry() {
|
||||||
@@ -23,13 +26,18 @@ func TestUserdataSkeleton_List(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, want := range []string{
|
for _, want := range []string{
|
||||||
"media/movies", "media/tv", "media/music", "media/audiobooks", "media/books",
|
"media/movies", "media/tv", "media/music", "media/audiobooks", "media/books",
|
||||||
"media/comics", "media/photos", "downloads", "import/paperless", "import/calibre",
|
"media/comics", "media/photos", "downloads",
|
||||||
"roms", "documents",
|
"roms", "documents",
|
||||||
} {
|
} {
|
||||||
if !got[want] {
|
if !got[want] {
|
||||||
t.Errorf("skeleton missing %q", want)
|
t.Errorf("skeleton missing %q", want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for _, gone := range []string{"import", "import/paperless", "import/calibre"} {
|
||||||
|
if got[gone] {
|
||||||
|
t.Errorf("carry-list must NOT hold %q — the drop-zone is canonical on the system drive (R-75)", gone)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestUserdataDir confirms the userdata root is a sibling under the namespace.
|
// TestUserdataDir confirms the userdata root is a sibling under the namespace.
|
||||||
|
|||||||
Reference in New Issue
Block a user