C6B-F1 cause 1 + §8: additive .fab export (HDD binds AND named volumes)

executeExport no longer either/or gates user data on needs_hdd — a needs_hdd
app bundles BOTH its HDD mounts and its named volumes (sonarr_config = the
whole app DB was silently dropped pre-fix). exportHDDData returns error and
fails LOUDLY on a basename collision between mounts (the manifest keys tars by
basename; the old code silently overwrote the first tar). EstimateExport made
additive to match, so the fits-on-dest gate counts both. Round-trip placement
test proves a userdata tar restores to <HDD_PATH>/userdata through the
untouched import mapping. Red-proofs recorded: either/or revert fails
scenario A; collision-check removal fails the collision test.
This commit is contained in:
2026-07-14 15:18:21 +02:00
parent 8967ba7cb4
commit c6d8bc82a2
4 changed files with 326 additions and 31 deletions
+15 -15
View File
@@ -44,7 +44,8 @@ func (e *Exporter) EstimateExport(stackName, destDrive string) (*ExportEstimate,
est.ConfigSizeHuman = humanizeBytes(est.ConfigSizeBytes)
e.debugf("EstimateExport: configSize=%s (%d bytes)", est.ConfigSizeHuman, est.ConfigSizeBytes)
// Data size: HDD bind mounts or Docker volumes
// Data size: HDD bind mounts PLUS Docker volumes. v0.130.0 (C6B-F1): additive, mirroring the
// export itself — a needs_hdd app bundles BOTH, so the fits-on-dest gate must count both.
if e.provider.GetStackNeedsHDD(stackName) {
mounts := e.provider.GetStackHDDMounts(stackName)
e.debugf("EstimateExport: HDD mounts: %v", mounts)
@@ -53,21 +54,20 @@ func (e *Exporter) EstimateExport(stackName, destDrive string) (*ExportEstimate,
e.debugf("EstimateExport: mount %s = %s", mount, humanizeBytes(mountSize))
est.DataSizeBytes += mountSize
}
} else {
volumes := e.provider.GetDockerVolumes(stackName)
e.debugf("EstimateExport: Docker volumes: %v", volumes)
for _, vol := range volumes {
volSize, err := volumeSizer(vol)
if err != nil {
// F-A: the controller runs containerized, so a failed helper read must not
// silently become 0-that-reads-as-fits. Mark unknown and keep going.
e.logger.Printf("[WARN] appexport: volume size unknown for %s: %v", vol, err)
est.SizeUnknown = true
continue
}
e.debugf("EstimateExport: volume %s = %s", vol, humanizeBytes(volSize))
est.DataSizeBytes += volSize
}
volumes := e.provider.GetDockerVolumes(stackName)
e.debugf("EstimateExport: Docker volumes: %v", volumes)
for _, vol := range volumes {
volSize, err := volumeSizer(vol)
if err != nil {
// F-A: the controller runs containerized, so a failed helper read must not
// silently become 0-that-reads-as-fits. Mark unknown and keep going.
e.logger.Printf("[WARN] appexport: volume size unknown for %s: %v", vol, err)
est.SizeUnknown = true
continue
}
e.debugf("EstimateExport: volume %s = %s", vol, humanizeBytes(volSize))
est.DataSizeBytes += volSize
}
if est.SizeUnknown {
est.DataSizeHuman = "ismeretlen méret"