Jämför commits
1
Incheckningar
| Upphovsman | SHA1 | Datum | |
|---|---|---|---|
|
|
724b9fb3e6 |
@@ -6,6 +6,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@@ -91,6 +92,31 @@ func TestBackupRefusesDestinationWithoutMarker(t *testing.T) {
|
|||||||
assertUntouched(ctx, t, repos, storeDir)
|
assertUntouched(ctx, t, repos, storeDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestBackupChecksMarkerBeforeDroppingIncompleteSnapshots checks that
|
||||||
|
// snapshot create writes nothing when the local index holds only an
|
||||||
|
// incomplete snapshot and the destination has no marker. The backup drops
|
||||||
|
// incomplete snapshots before it scans; checked after that, the local
|
||||||
|
// index would hold none and nothing would be checked.
|
||||||
|
func TestBackupChecksMarkerBeforeDroppingIncompleteSnapshots(t *testing.T) {
|
||||||
|
log.Initialize(log.Config{})
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
storeDir := t.TempDir()
|
||||||
|
v, repos := newFileDestinationVaultik(ctx, t, storeDir)
|
||||||
|
|
||||||
|
require.NoError(t, repos.Snapshots.Create(ctx, nil, &database.Snapshot{
|
||||||
|
ID: "host_first_2026-01-02T03:04:05Z",
|
||||||
|
Hostname: "host",
|
||||||
|
StartedAt: time.Date(2026, 1, 2, 3, 4, 5, 0, time.UTC),
|
||||||
|
}))
|
||||||
|
|
||||||
|
err := backUp(v, "first")
|
||||||
|
require.ErrorContains(t, err, markerFile)
|
||||||
|
|
||||||
|
assertUntouched(ctx, t, repos, storeDir)
|
||||||
|
}
|
||||||
|
|
||||||
// TestPruneRefusesDestinationWithoutMarker checks that prune fails and
|
// TestPruneRefusesDestinationWithoutMarker checks that prune fails and
|
||||||
// deletes no local snapshot record when the destination has no marker.
|
// deletes no local snapshot record when the destination has no marker.
|
||||||
func TestPruneRefusesDestinationWithoutMarker(t *testing.T) {
|
func TestPruneRefusesDestinationWithoutMarker(t *testing.T) {
|
||||||
@@ -147,6 +173,35 @@ func TestRemoveSnapshotWarnsWhenDestinationHasNoMarker(t *testing.T) {
|
|||||||
"Removed snapshot metadata from remote storage")
|
"Removed snapshot metadata from remote storage")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestRemoveSnapshotOnlyOnDestinationFailsWithoutMarker checks that
|
||||||
|
// snapshot remove, for a snapshot that is only on the destination, fails
|
||||||
|
// saying the destination is not present when it has no marker, instead
|
||||||
|
// of saying the snapshot does not exist.
|
||||||
|
func TestRemoveSnapshotOnlyOnDestinationFailsWithoutMarker(t *testing.T) {
|
||||||
|
log.Initialize(log.Config{})
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
storeDir := filepath.Join(t.TempDir(), "mnt")
|
||||||
|
v, repos, _ := backUpToFileDestination(ctx, t, storeDir)
|
||||||
|
require.NoError(t, backUp(v, "second"))
|
||||||
|
|
||||||
|
id := localSnapshotID(ctx, t, repos, "first")
|
||||||
|
_, err := v.RemoveSnapshot(id,
|
||||||
|
&vaultik.RemoveOptions{Force: true, LocalOnly: true})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Unplug the volume: an empty directory stays at its mount point.
|
||||||
|
require.NoError(t, os.Rename(storeDir, storeDir+"-unplugged"))
|
||||||
|
require.NoError(t, os.Mkdir(storeDir, 0o750))
|
||||||
|
|
||||||
|
_, err = v.RemoveSnapshot(id, &vaultik.RemoveOptions{Force: true})
|
||||||
|
require.ErrorContains(t, err, markerFile)
|
||||||
|
require.ErrorContains(t, err, "mount the volume")
|
||||||
|
|
||||||
|
assertUntouched(ctx, t, repos, storeDir)
|
||||||
|
}
|
||||||
|
|
||||||
// TestListSnapshotsWarnsWhenDestinationHasNoMarker checks that snapshot
|
// TestListSnapshotsWarnsWhenDestinationHasNoMarker checks that snapshot
|
||||||
// list warns and shows the local index alone when the destination has no
|
// list warns and shows the local index alone when the destination has no
|
||||||
// marker, without reporting the local snapshot as missing from it.
|
// marker, without reporting the local snapshot as missing from it.
|
||||||
|
|||||||
@@ -1330,7 +1330,9 @@ func (v *Vaultik) RemoveSnapshot(
|
|||||||
// remove: no row and, unless LocalOnly is set, no metadata on the
|
// remove: no row and, unless LocalOnly is set, no metadata on the
|
||||||
// destination store either. The destination store is consulted only
|
// destination store either. The destination store is consulted only
|
||||||
// when the local index has no row, so a snapshot in the local index can
|
// when the local index has no row, so a snapshot in the local index can
|
||||||
// still be removed from it while the destination store is unreachable.
|
// still be removed from it while the destination store is unreachable,
|
||||||
|
// and only after checkDestinationMarker passes, so an unplugged volume's
|
||||||
|
// empty mount point is not taken for a store without the snapshot.
|
||||||
func (v *Vaultik) findSnapshotToRemove(
|
func (v *Vaultik) findSnapshotToRemove(
|
||||||
snapshotID string, opts *RemoveOptions,
|
snapshotID string, opts *RemoveOptions,
|
||||||
) (bool, error) {
|
) (bool, error) {
|
||||||
@@ -1350,6 +1352,11 @@ func (v *Vaultik) findSnapshotToRemove(
|
|||||||
errSnapshotNotFound, snapshotID)
|
errSnapshotNotFound, snapshotID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err := v.checkDestinationMarker()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
objects, err := v.remoteSnapshotObjects(snapshot.RemoteSnapshotKey(snapshotID))
|
objects, err := v.remoteSnapshotObjects(snapshot.RemoteSnapshotKey(snapshotID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, fmt.Errorf("looking up snapshot on destination store: %w", err)
|
return false, fmt.Errorf("looking up snapshot on destination store: %w", err)
|
||||||
|
|||||||
Referens i nytt ärende
Block a user