fix: preserve snapshot symlinks and guard incomplete upgrade copies
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -122,6 +123,12 @@ func TestSnapshotFullRestore(t *testing.T) {
|
||||
for _, running := range []bool{false, true} {
|
||||
t.Run(fmt.Sprint(running), func(t *testing.T) {
|
||||
a, i, home, id := snapshotRestoreFixture(t, running, "")
|
||||
links := map[string]string{"absolute": "/workspace/example", "dangling": "missing", "loop": "loop", "relative": "data"}
|
||||
for name, target := range links {
|
||||
if err := os.Symlink(target, filepath.Join(filepath.Dir(home), id, name)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if err := a.recordSnapshot(i, id, i.Version, i.ImageID); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -140,6 +147,12 @@ func TestSnapshotFullRestore(t *testing.T) {
|
||||
}
|
||||
}
|
||||
current, _ := a.s.GetInstance(i.ID)
|
||||
for name, target := range links {
|
||||
got, err := os.Readlink(filepath.Join(home, name))
|
||||
if err != nil || got != target {
|
||||
t.Fatalf("restored link %s: %q %v", name, got, err)
|
||||
}
|
||||
}
|
||||
want := "exited"
|
||||
if running {
|
||||
want = "running"
|
||||
@@ -169,6 +182,40 @@ func TestSnapshotRestoreRollback(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIncompleteSnapshotNotRestorable(t *testing.T) {
|
||||
a, i, _, id := snapshotRestoreFixture(t, false, "")
|
||||
if _, err := a.s.DB.Exec("CREATE TABLE snapshot_copy_state (instance_id TEXT, id TEXT, ready INTEGER)"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := a.s.DB.Exec("INSERT INTO snapshot_copy_state VALUES(?,?,0)", i.ID, id); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
w := configCall(a, i, "GET", "snapshots", "")
|
||||
var list []snapshotInfo
|
||||
if w.Code != 200 || json.Unmarshal(w.Body.Bytes(), &list) != nil || len(list) != 1 || list[0].Restorable || list[0].RestoreReason != "snapshot copy incomplete" {
|
||||
t.Fatal(w.Code, w.Body.String())
|
||||
}
|
||||
w = configCall(a, i, "POST", "snapshots", snapshotRestoreBody(id, true))
|
||||
if w.Code != 409 || !strings.Contains(w.Body.String(), "snapshot copy incomplete") {
|
||||
t.Fatal(w.Code, w.Body.String())
|
||||
}
|
||||
if err := a.recordSnapshot(i, id, i.Version, i.ImageID); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := a.s.DB.Exec("UPDATE snapshot_copy_state SET ready=1"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, _, reason := a.snapshotCompatibility(i, id); reason != "" {
|
||||
t.Fatal(reason)
|
||||
}
|
||||
if _, err := a.s.DB.Exec("ALTER TABLE snapshot_copy_state RENAME COLUMN ready TO unavailable"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, _, reason := a.snapshotCompatibility(i, id); reason != "snapshot completion metadata unavailable" {
|
||||
t.Fatal(reason)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSnapshotRestoreGuards(t *testing.T) {
|
||||
for _, test := range []string{"unknown", "mismatch", "password", "confirm", "reader", "quota", "error", "unsafe", "foreign", "oversized"} {
|
||||
t.Run(test, func(t *testing.T) {
|
||||
@@ -196,7 +243,7 @@ func TestSnapshotRestoreGuards(t *testing.T) {
|
||||
case "error":
|
||||
a.s.SetInstance(i.ID, "error")
|
||||
case "unsafe":
|
||||
if err := os.Symlink("/etc/passwd", filepath.Join(filepath.Dir(home), id, "unsafe")); err != nil {
|
||||
if err := syscall.Mkfifo(filepath.Join(filepath.Dir(home), id, "unsafe"), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want = 400
|
||||
|
||||
Reference in New Issue
Block a user