Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c468504dc | ||
|
|
b30e79ee45 |
@@ -609,6 +609,10 @@ Work planned after 1.0. Loosely ordered by priority.
|
||||
doesn't resume from where it stopped or skip already-present
|
||||
files. A `--resume` mode that checks targets before fetching
|
||||
blobs would matter for very large restores.
|
||||
* **Daemon mode.** A long-running mode that watches for file
|
||||
changes so frequent backups, such as hourly, skip the full scan.
|
||||
It adds little for the usual runs from cron every 12 to 36 hours.
|
||||
See [issue #204](https://git.eeqj.de/sneak/vaultik/issues/204).
|
||||
|
||||
### usability
|
||||
|
||||
|
||||
@@ -680,18 +680,10 @@ func faultScannerFactory(
|
||||
|
||||
// Scenario 5: the restore target runs out of space mid-file. Restore
|
||||
// must fail with an out-of-space error, and must not leave a truncated
|
||||
// file at the target path presenting as a complete restore. Restore
|
||||
// today writes each file straight to its final path and does not remove
|
||||
// it when a write fails, so the truncated file survives; deleting it is
|
||||
// tracked by https://git.eeqj.de/sneak/vaultik/issues/163. Skipped until
|
||||
// that lands, so the destination assertion below is recorded rather than
|
||||
// dropped.
|
||||
// file at the target path presenting as a complete restore.
|
||||
//
|
||||
//nolint:paralleltest // installs the global logger via log.Initialize
|
||||
func TestRestoreReportsDiskFull(t *testing.T) {
|
||||
t.Skip("blocked on https://git.eeqj.de/sneak/vaultik/issues/163: " +
|
||||
"a disk-full write leaves a truncated file at the target path " +
|
||||
"instead of removing it")
|
||||
log.Initialize(log.Config{})
|
||||
|
||||
osFS := afero.NewOsFs()
|
||||
@@ -717,10 +709,10 @@ func TestRestoreReportsDiskFull(t *testing.T) {
|
||||
id := fullFaultBackup(ctx, t, osFS, inner, cfg, repos, dataDir, dbPath, "diskfull")
|
||||
require.NoError(t, db.Close())
|
||||
|
||||
// Restore onto a filesystem that allows only a few bytes of file
|
||||
// content: enough to create files, far too little to hold them.
|
||||
// Restore onto a target that allows only a few bytes of file content:
|
||||
// enough to create files, far too little to hold them.
|
||||
budget := int64(8)
|
||||
quota := "aFS{Fs: osFS, remaining: &budget}
|
||||
quota := "aFS{Fs: osFS, dir: restoreDir, remaining: &budget}
|
||||
|
||||
v := newReaderVaultik(ctx, cfg, inner, nil, quota)
|
||||
err = v.Restore(&vaultik.RestoreOptions{SnapshotID: id, TargetDir: restoreDir})
|
||||
@@ -754,21 +746,26 @@ func assertRestoredTree(
|
||||
// budget is exhausted, mirroring a real ENOSPC.
|
||||
var errNoSpace = errors.New("no space left on device")
|
||||
|
||||
// quotaFS is an afero.Fs whose files may write only a fixed total number
|
||||
// of content bytes before failing, simulating a full restore target. It
|
||||
// wraps the interface so every method except Create delegates to the
|
||||
// real filesystem; only file writes are capped.
|
||||
// quotaFS is an afero.Fs on which files opened under dir may write only
|
||||
// a fixed total number of content bytes before failing, simulating a full
|
||||
// restore target. Every other method, and every file outside dir, goes
|
||||
// straight to the real filesystem: restore also writes the decrypted
|
||||
// metadata database under $TMPDIR through this filesystem, and capping
|
||||
// that would fail the restore before it wrote anything to the target.
|
||||
type quotaFS struct {
|
||||
afero.Fs
|
||||
|
||||
dir string
|
||||
remaining *int64
|
||||
}
|
||||
|
||||
//nolint:ireturn // afero.Fs.Create's signature requires returning afero.File.
|
||||
func (q *quotaFS) Create(name string) (afero.File, error) {
|
||||
f, err := q.Fs.Create(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
//nolint:ireturn // afero.Fs.OpenFile's signature requires returning afero.File.
|
||||
func (q *quotaFS) OpenFile(
|
||||
name string, flag int, perm os.FileMode,
|
||||
) (afero.File, error) {
|
||||
f, err := q.Fs.OpenFile(name, flag, perm)
|
||||
if err != nil || !strings.HasPrefix(name, q.dir) {
|
||||
return f, err
|
||||
}
|
||||
|
||||
return "aFile{File: f, remaining: q.remaining}, nil
|
||||
|
||||
Reference in New Issue
Block a user