Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf8bf5f49c |
No files matched your search
@@ -245,17 +245,6 @@ func (p *Packer) Flush(ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DiscardCurrentBlob closes and removes the temp file of the blob being
|
|
||||||
// packed, if any, and drops that blob. A caller that stops before Flush
|
|
||||||
// must call it, or the temp file is left behind. Thread-safe.
|
|
||||||
func (p *Packer) DiscardCurrentBlob() {
|
|
||||||
p.mu.Lock()
|
|
||||||
defer p.mu.Unlock()
|
|
||||||
|
|
||||||
p.cleanupTempFile()
|
|
||||||
p.currentBlob = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FinalizeBlob finalizes the current blob being assembled.
|
// FinalizeBlob finalizes the current blob being assembled.
|
||||||
// This compresses the accumulated chunks, encrypts the result, and computes
|
// This compresses the accumulated chunks, encrypts the result, and computes
|
||||||
// the content-addressed hash. The finalized blob is either passed to the
|
// the content-addressed hash. The finalized blob is either passed to the
|
||||||
|
|||||||
@@ -1295,11 +1295,6 @@ func (s *Scanner) batchAddFilesToSnapshot(
|
|||||||
func (s *Scanner) processPhase(
|
func (s *Scanner) processPhase(
|
||||||
ctx context.Context, filesToProcess []*FileToProcess, result *ScanResult,
|
ctx context.Context, filesToProcess []*FileToProcess, result *ScanResult,
|
||||||
) error {
|
) error {
|
||||||
// On an error or cancellation the blob being packed is never
|
|
||||||
// finalized, so its temp file must be removed here. After a
|
|
||||||
// successful flush there is no such blob and this does nothing.
|
|
||||||
defer s.packer.DiscardCurrentBlob()
|
|
||||||
|
|
||||||
// Calculate total bytes to process
|
// Calculate total bytes to process
|
||||||
var totalBytes int64
|
var totalBytes int64
|
||||||
for _, f := range filesToProcess {
|
for _, f := range filesToProcess {
|
||||||
|
|||||||
@@ -381,60 +381,3 @@ func TestScannerCancelStopsSkipErrorsRun(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestScannerRemovesBlobTempFileWhenRunStops checks that a run stopped
|
|
||||||
// part-way, by a file it cannot read or by a cancellation, removes the
|
|
||||||
// temp file of the blob it was packing.
|
|
||||||
func TestScannerRemovesBlobTempFileWhenRunStops(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
// a.txt is backed up first and starts a blob; the run stops at b.txt.
|
|
||||||
const target = "/source/b.txt"
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
newFs func(cancel context.CancelFunc) afero.Fs
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "file cannot be read",
|
|
||||||
newFs: func(context.CancelFunc) afero.Fs {
|
|
||||||
return &readFailFs{Fs: afero.NewMemMapFs(), target: target}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "cancelled",
|
|
||||||
newFs: func(cancel context.CancelFunc) afero.Fs {
|
|
||||||
return &cancelOnOpenFs{
|
|
||||||
Fs: afero.NewMemMapFs(), target: target, cancel: cancel,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
fs := tt.newFs(cancel)
|
|
||||||
writeSkipErrorTestFile(t, fs, "/source/a.txt", "first file content")
|
|
||||||
writeSkipErrorTestFile(t, fs, target, "second file content")
|
|
||||||
|
|
||||||
_, err := runSkipErrorScan(ctx, t, fs, "/source", false, nil)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("expected the scan to stop with an error, got nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
left, err := afero.Glob(fs, filepath.Join(os.TempDir(), "vaultik-blob-*"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("listing blob temp files: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(left) != 0 {
|
|
||||||
t.Fatalf("expected no blob temp file left, got %v", left)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -268,81 +268,6 @@ func TestShallowVerifyReportsStatErrorsApartFromMissing(t *testing.T) {
|
|||||||
require.Contains(t, text, "FAILED")
|
require.Contains(t, text, "FAILED")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestShallowVerifyQuietFailsOnStatErrorAlone backs up a healthy snapshot,
|
|
||||||
// then makes storage fail Stat, with an error other than not-found, for
|
|
||||||
// one blob alone and then for the encrypted database alone. Each run must
|
|
||||||
// fail verify, and under --quiet, which drops the report, the error
|
|
||||||
// storage returned must still reach stderr.
|
|
||||||
//
|
|
||||||
//nolint:paralleltest // captureProcessStderr replaces os.Stderr
|
|
||||||
func TestShallowVerifyQuietFailsOnStatErrorAlone(t *testing.T) {
|
|
||||||
log.Initialize(log.Config{})
|
|
||||||
|
|
||||||
fs := afero.NewOsFs()
|
|
||||||
tempDir := t.TempDir()
|
|
||||||
|
|
||||||
dataDir := filepath.Join(tempDir, "source")
|
|
||||||
storeDir := filepath.Join(tempDir, "remote")
|
|
||||||
dbPath := filepath.Join(tempDir, "index.sqlite")
|
|
||||||
|
|
||||||
chunkSize := int64(32 * 1024)
|
|
||||||
maxBlobSize := int64(128 * 1024)
|
|
||||||
|
|
||||||
require.NoError(t, fs.MkdirAll(dataDir, 0o755))
|
|
||||||
require.NoError(t, afero.WriteFile(fs,
|
|
||||||
filepath.Join(dataDir, "data.bin"),
|
|
||||||
bytesPattern("shallow-quiet-", int(maxBlobSize*2)), 0o644))
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
cfg, storer, snapshotID := runFileStorageBackup(
|
|
||||||
ctx, t, fs, dataDir, storeDir, dbPath, chunkSize, maxBlobSize)
|
|
||||||
|
|
||||||
newQuietVerifier := func(s storage.Storer) *vaultik.Vaultik {
|
|
||||||
v := &vaultik.Vaultik{
|
|
||||||
Config: cfg,
|
|
||||||
Storage: s,
|
|
||||||
Fs: fs,
|
|
||||||
Stdout: io.Discard,
|
|
||||||
Stderr: io.Discard,
|
|
||||||
UI: ui.NewWithColor(io.Discard, false),
|
|
||||||
}
|
|
||||||
v.SetContext(ctx)
|
|
||||||
v.UI.SetQuiet(true)
|
|
||||||
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
require.NoError(t,
|
|
||||||
newQuietVerifier(storer).VerifySnapshotWithOptions(
|
|
||||||
snapshotID, &vaultik.VerifyOptions{}),
|
|
||||||
"shallow verify should pass on a healthy snapshot")
|
|
||||||
|
|
||||||
blobKeys := storedBlobKeys(t, storeDir)
|
|
||||||
require.NotEmpty(t, blobKeys)
|
|
||||||
|
|
||||||
dbKey := "metadata/" + snapshot.RemoteSnapshotKey(snapshotID) + "/db.zst.age"
|
|
||||||
|
|
||||||
for _, failKey := range []string{blobKeys[0], dbKey} {
|
|
||||||
v := newQuietVerifier(statFailingStorer{
|
|
||||||
Storer: storer,
|
|
||||||
failKeys: []string{failKey},
|
|
||||||
})
|
|
||||||
|
|
||||||
var err error
|
|
||||||
|
|
||||||
stderr := captureProcessStderr(t, func() {
|
|
||||||
err = v.VerifySnapshotWithOptions(
|
|
||||||
snapshotID, &vaultik.VerifyOptions{})
|
|
||||||
})
|
|
||||||
|
|
||||||
require.Error(t, err,
|
|
||||||
"verify must fail when %s could not be checked", failKey)
|
|
||||||
require.Contains(t, stderr, errStatDenied.Error(),
|
|
||||||
"under --quiet the error for %s must still reach stderr", failKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// storedBlobKeys returns the storage key of every blob file under
|
// storedBlobKeys returns the storage key of every blob file under
|
||||||
// storeDir, sorted.
|
// storeDir, sorted.
|
||||||
func storedBlobKeys(t *testing.T, storeDir string) []string {
|
func storedBlobKeys(t *testing.T, storeDir string) []string {
|
||||||
|
|||||||
@@ -911,19 +911,9 @@ func (v *Vaultik) formatVerifyResult(
|
|||||||
return v.outputVerifyJSON(result)
|
return v.outputVerifyJSON(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// --quiet drops the report but not the errors storage returned, which
|
// Under --quiet a failure is still returned, and the cli layer
|
||||||
// are logged on stderr. A failure is still returned either way, and
|
// prints it on stderr.
|
||||||
// the cli layer prints it on stderr.
|
if !v.UI.Quiet() {
|
||||||
if v.UI.Quiet() {
|
|
||||||
for _, blob := range result.Unchecked {
|
|
||||||
log.Warn("Could not check blob", "blob", blob.Hash, "error", blob.Error)
|
|
||||||
}
|
|
||||||
|
|
||||||
if result.DatabaseCheckError != "" {
|
|
||||||
log.Warn("Could not check the encrypted database",
|
|
||||||
"snapshot", result.SnapshotID, "error", result.DatabaseCheckError)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
v.printVerifySummary(result, failure)
|
v.printVerifySummary(result, failure)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user