968c968559
gates / gates (push) Successful in 11s
writeSafetyDump called DumpOne into the app's OWN unit dir and renamed the result to pre-restore-* afterwards. DumpOne writes <stack>-<dbtype>.sql - the app's canonical dump - so every safety dump overwrote the app's real backup and then moved it away, leaving the app with no database backup until the next nightly run. A local restore-from-unit in that window tells the customer the app never had a database. The comment beside it asserted the rename meant it 'can never overwrite the app's real dump'. False as written, and believed for four months. Measured live before the fix: docmost and bookstack each held only pre-restore-* files and no canonical dump. DumpOneTo takes the final path and derives its own .tmp from it. DumpOne keeps its signature and calls it with the canonical name. writeSafetyDump asks for its own name directly; the rename is gone; the comment now states the invariant and how it is enforced. db_dumps no longer lists the undo copies. All three consumers of Manifest.DBDumps were grepped and named - all inside recovery_unit.go, none reads it for recovery. The files are neither deleted nor hidden. Tests 1485 -> 1493. FIVE red-proofs, TWO PASSED first time and both are reported: the behavioural tests inject the dump seam so a mutation inside DumpOneTo was invisible, and 1.3 had no test at all. Guards added at the layer each defect lives in; both mutations then convicted.
170 lines
6.0 KiB
Go
170 lines
6.0 KiB
Go
package backup
|
|
|
|
// This file bridges the backup package to internal/appbackup, where the
|
|
// self-contained app-data backup primitives (DB dump, Docker-volume archive
|
|
// discovery, keep-side path helpers) now live. The backup package keeps these
|
|
// names available — via type/const aliases and thin function forwarders — so
|
|
// the (still present) delete-side code and the both-side consumers (web, api,
|
|
// report) compile unchanged. Behaviour is identical: the forwarders call
|
|
// straight through to appbackup.
|
|
//
|
|
// Go has no function aliasing, so the functions are one-line forwarders while
|
|
// the types/consts use real aliases.
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
"time"
|
|
|
|
"gitea.dooplex.hu/admin/felhom-controller/internal/appbackup"
|
|
)
|
|
|
|
// --- type aliases (appdata) ---
|
|
|
|
type StackDataProvider = appbackup.StackDataProvider
|
|
type StackSummary = appbackup.StackSummary
|
|
type AppBackupInfo = appbackup.AppBackupInfo
|
|
type AppDataPath = appbackup.AppDataPath
|
|
type AppDockerVolume = appbackup.AppDockerVolume
|
|
type RecoveryInfo = appbackup.RecoveryInfo
|
|
type ClassifiedBind = appbackup.ClassifiedBind
|
|
|
|
// --- type aliases (dbdump) ---
|
|
|
|
type DBType = appbackup.DBType
|
|
type DiscoveredDB = appbackup.DiscoveredDB
|
|
type DumpResult = appbackup.DumpResult
|
|
type DumpValidation = appbackup.DumpValidation
|
|
type DumpFileInfo = appbackup.DumpFileInfo
|
|
|
|
// --- const aliases ---
|
|
|
|
const (
|
|
DBTypePostgres = appbackup.DBTypePostgres
|
|
DBTypeMariaDB = appbackup.DBTypeMariaDB
|
|
)
|
|
|
|
// Backup-classification class constants (Task 3-core) — aliased so the tier engines can switch on
|
|
// class without importing appbackup directly.
|
|
const (
|
|
ClassMandatory = appbackup.ClassMandatory
|
|
ClassOptional = appbackup.ClassOptional
|
|
ClassExcluded = appbackup.ClassExcluded
|
|
)
|
|
|
|
// FelhomDataDir is the namespace directory on storage drives for all felhom-managed data.
|
|
const FelhomDataDir = appbackup.FelhomDataDir
|
|
|
|
// --- function forwarders (dbdump) ---
|
|
|
|
func DiscoverDatabases(ctx context.Context, logger *log.Logger, debug bool, knownStacks []string) ([]DiscoveredDB, error) {
|
|
return appbackup.DiscoverDatabases(ctx, logger, debug, knownStacks)
|
|
}
|
|
|
|
func DumpAll(ctx context.Context, dbs []DiscoveredDB, dumpDir string, logger *log.Logger, debug bool) []DumpResult {
|
|
return appbackup.DumpAll(ctx, dbs, dumpDir, logger, debug)
|
|
}
|
|
|
|
func DumpOne(ctx context.Context, db DiscoveredDB, dumpDir string, logger *log.Logger, debug bool) DumpResult {
|
|
return appbackup.DumpOne(ctx, db, dumpDir, logger, debug)
|
|
}
|
|
|
|
// DumpOneTo dumps to an EXPLICIT final path (R-361). The safety dump uses it so it never names — and
|
|
// therefore never destroys — the app's own `<stack>-<dbtype>.sql`.
|
|
func DumpOneTo(ctx context.Context, db DiscoveredDB, finalPath string, logger *log.Logger, debug bool) DumpResult {
|
|
return appbackup.DumpOneTo(ctx, db, finalPath, logger, debug)
|
|
}
|
|
|
|
// ImportDump replays a captured .sql dump back into a running DB container (F17 restore path).
|
|
func ImportDump(ctx context.Context, db DiscoveredDB, dumpPath string, logger *log.Logger, debug bool) error {
|
|
return appbackup.ImportDump(ctx, db, dumpPath, logger, debug)
|
|
}
|
|
|
|
func ValidateDump(filePath string, dbType DBType) DumpValidation {
|
|
return appbackup.ValidateDump(filePath, dbType)
|
|
}
|
|
|
|
func ListDumpFiles(dumpDir string, cached func(name string, size int64, mod time.Time) (DumpValidation, bool)) ([]DumpFileInfo, error) {
|
|
return appbackup.ListDumpFiles(dumpDir, cached)
|
|
}
|
|
|
|
// --- function forwarders (appdata) ---
|
|
|
|
func DiscoverAppData(provider StackDataProvider, discoveredDBs []DiscoveredDB) []AppBackupInfo {
|
|
return appbackup.DiscoverAppData(provider, discoveredDBs)
|
|
}
|
|
|
|
func ParseComposeNamedVolumes(composePath string) []AppDockerVolume {
|
|
return appbackup.ParseComposeNamedVolumes(composePath)
|
|
}
|
|
|
|
func ResolveDockerVolumeNames(composePath string) []string {
|
|
return appbackup.ResolveDockerVolumeNames(composePath)
|
|
}
|
|
|
|
func ParseComposeImages(composePath string) []string {
|
|
return appbackup.ParseComposeImages(composePath)
|
|
}
|
|
|
|
// DBServiceNames forwards to appbackup.DBServiceNames — the compose SERVICE names holding a database,
|
|
// i.e. the argument list for the DB-only bring-up both restore paths use before a dump replay (R-47).
|
|
func DBServiceNames(composePath string) ([]string, error) {
|
|
return appbackup.DBServiceNames(composePath)
|
|
}
|
|
|
|
// humanizeBytes forwards to appbackup.HumanizeBytes; kept unexported so the
|
|
// many in-package call sites (backup.go, crossdrive.go, restore code) need no edit.
|
|
func humanizeBytes(b int64) string {
|
|
return appbackup.HumanizeBytes(b)
|
|
}
|
|
|
|
// --- function forwarders (paths) ---
|
|
//
|
|
// NOTE: the path helpers below take a felhom-data NAMESPACE ROOT, not a bare drive path. Use
|
|
// NamespaceRoot (or Manager.namespaceRoot / Manager.AppNamespaceRoot) to resolve the root first.
|
|
|
|
func NamespaceRoot(drivePath string, inGuestDrive bool) string {
|
|
return appbackup.NamespaceRoot(drivePath, inGuestDrive)
|
|
}
|
|
|
|
// NamespaceRootFor re-exports the ONE drive-kind-aware resolver (R-203).
|
|
func NamespaceRootFor(drivePath, systemDataPath string) string {
|
|
return appbackup.NamespaceRootFor(drivePath, systemDataPath)
|
|
}
|
|
|
|
func PrimaryBackupPath(nsRoot string) string {
|
|
return appbackup.PrimaryBackupPath(nsRoot)
|
|
}
|
|
|
|
func AppDBDumpPath(nsRoot, stackName string) string {
|
|
return appbackup.AppDBDumpPath(nsRoot, stackName)
|
|
}
|
|
|
|
func AppVolumeDumpPath(nsRoot, stackName string) string {
|
|
return appbackup.AppVolumeDumpPath(nsRoot, stackName)
|
|
}
|
|
|
|
func RecoveryUnitPath(nsRoot, stackName string) string {
|
|
return appbackup.RecoveryUnitPath(nsRoot, stackName)
|
|
}
|
|
|
|
func RecoveryUnitComposePath(nsRoot, stackName string) string {
|
|
return appbackup.RecoveryUnitComposePath(nsRoot, stackName)
|
|
}
|
|
|
|
func RecoveryUnitManifestPath(nsRoot, stackName string) string {
|
|
return appbackup.RecoveryUnitManifestPath(nsRoot, stackName)
|
|
}
|
|
|
|
func AppDataDir(nsRoot, stackName string) string {
|
|
return appbackup.AppDataDir(nsRoot, stackName)
|
|
}
|
|
|
|
func AppDataDirNames(hddPath, stackName string, hddMounts []string) []string {
|
|
return appbackup.AppDataDirNames(hddPath, stackName, hddMounts)
|
|
}
|
|
|
|
func AppDataBindsPresent(hddPath string, hddMounts []string) bool {
|
|
return appbackup.AppDataBindsPresent(hddPath, hddMounts)
|
|
}
|