feat(samba): backup classification from the shares registry (R-7 slice 1, Part 4)
ClassifiedBinds("samba") resolves from the shares registry instead of catalog
metadata (samba has no .felhom.yml; its binds are absolute share paths). [R4]
Offsite ON -> mandatory (offsite + tier-2); OFF -> optional (tier-2 only);
smb.conf/passdb never classified. Verified through the REAL ComputeCaptureSet
tier filter incl. the negative (optional NOT in offsite). Zero engine edits.
Part-4 Step-1 finding: tier-2 (RunTier2) short-circuits on os.Stat(unitDir)
BEFORE GetStackClassifiedBinds, and the offsite runner enumerates
settings.GetOffboxApps() — both are recovery-unit shaped, which a share-only
infra stack has not. Wiring share data into a live run is therefore more than an
enumeration tweak; reported as a design fork per the STOP clause, not improvised.
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
package stacks
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/appbackup"
|
||||
)
|
||||
|
||||
// Backup classification for the samba stack (R-7 slice 1, [R4]).
|
||||
//
|
||||
// samba is CONTROLLER-GENERATED infra: it has no .felhom.yml and its compose binds are absolute
|
||||
// share paths, so the catalog path (LoadMetadata → ParseComposeClassifiableBinds → ClassifyBinds)
|
||||
// cannot classify it. Its classification comes from the SHARES REGISTRY instead:
|
||||
//
|
||||
// share.Offsite == true → ClassMandatory → TierOffsite (offsite) AND TierSecondary (tier-2)
|
||||
// share.Offsite == false → ClassOptional → TierSecondary only (never offsite)
|
||||
//
|
||||
// The smb.conf / passdb mounts are deliberately NOT emitted — they are config, not customer data.
|
||||
//
|
||||
// SCOPE NOTE (the reported design fork, see REPORT.md): making this seam correct does NOT by itself
|
||||
// put share data into a live tier-2/offsite run. Both engines are structured around a per-app
|
||||
// RECOVERY UNIT — backup.RunTier2 short-circuits on `os.Stat(unitDir)` before it ever calls
|
||||
// GetStackClassifiedBinds, and the offsite runner enumerates settings.GetOffboxApps(). A share-only
|
||||
// infra stack has neither, and teaching them about one is more than an enumeration tweak, so per the
|
||||
// task's STOP clause it was reported rather than improvised inside the engines.
|
||||
func (m *Manager) sambaClassifiedBinds() ([]appbackup.ClassifiedBind, bool) {
|
||||
if m.settings == nil {
|
||||
return nil, false
|
||||
}
|
||||
shares := m.settings.GetSMBShares()
|
||||
out := make([]appbackup.ClassifiedBind, 0, len(shares))
|
||||
for _, sh := range shares {
|
||||
cls := appbackup.ClassOptional
|
||||
if sh.Offsite {
|
||||
cls = appbackup.ClassMandatory
|
||||
}
|
||||
out = append(out, appbackup.ClassifiedBind{
|
||||
ComposeBind: appbackup.ComposeBind{
|
||||
Root: appbackup.RootHDD,
|
||||
RelPath: m.shareRelPath(sh.Path),
|
||||
ReadOnly: sh.ReadOnly,
|
||||
},
|
||||
Class: cls,
|
||||
Origin: appbackup.OriginExplicit, // the customer's per-share Felhőmentés toggle IS explicit
|
||||
})
|
||||
}
|
||||
return out, true
|
||||
}
|
||||
|
||||
// shareRelPath expresses a share's absolute path relative to its owning registered storage root, so
|
||||
// the emitted bind matches the (Root, RelPath) + root-path resolution shape the capture-set helpers
|
||||
// use. A share under no registered root falls back to the path minus its leading slash (never
|
||||
// absolute — the structural guards reject absolute RelPaths).
|
||||
func (m *Manager) shareRelPath(path string) string {
|
||||
p := filepath.ToSlash(path)
|
||||
if m.settings != nil {
|
||||
for _, sp := range m.settings.GetStoragePaths() {
|
||||
root := filepath.ToSlash(sp.Path)
|
||||
if strings.HasPrefix(p, root+"/") {
|
||||
return strings.TrimPrefix(p, root+"/")
|
||||
}
|
||||
}
|
||||
}
|
||||
return strings.TrimPrefix(p, "/")
|
||||
}
|
||||
Reference in New Issue
Block a user