117 lines
3.8 KiB
Go
117 lines
3.8 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestRestoreSnapshotCopyFailureDoesNotConsumeRetention(t *testing.T) {
|
|
if os.Geteuid() != 0 {
|
|
t.Skip("requires chown")
|
|
}
|
|
a, i, home := configTestAPI(t, false)
|
|
configWrite(t, filepath.Join(home, ".credentials.yaml"), "archive")
|
|
w := configCall(a, i, "POST", "backups", `{"password":"original-password"}`)
|
|
var result struct{ ID string }
|
|
if w.Code != 200 || json.Unmarshal(w.Body.Bytes(), &result) != nil {
|
|
t.Fatal(w.Code, w.Body.String())
|
|
}
|
|
configWrite(t, filepath.Join(home, ".credentials.yaml"), "live")
|
|
base := filepath.Dir(home)
|
|
for _, name := range []string{"pre-restore-valuable", "failed-restore-valuable"} {
|
|
if err := os.Mkdir(filepath.Join(base, name), 0700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
configWrite(t, filepath.Join(base, name, "keep"), name)
|
|
}
|
|
if err := os.Symlink("missing-target", filepath.Join(home, "dangling")); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
body := fmt.Sprintf(`{"backup_id":%q,"password":"original-password"}`, result.ID)
|
|
for attempt := 0; attempt < 3; attempt++ {
|
|
w = configCall(a, i, "POST", "restore", body)
|
|
if w.Code != 400 || !strings.Contains(w.Body.String(), "cannot snapshot home") {
|
|
t.Fatalf("attempt %d: %d %s", attempt, w.Code, w.Body.String())
|
|
}
|
|
entries, err := os.ReadDir(base)
|
|
if err != nil || len(entries) != 3 {
|
|
t.Fatalf("partial snapshot leaked: %v %v", entries, err)
|
|
}
|
|
for _, name := range []string{"pre-restore-valuable", "failed-restore-valuable"} {
|
|
data, err := os.ReadFile(filepath.Join(base, name, "keep"))
|
|
if err != nil || string(data) != name {
|
|
t.Fatalf("valuable snapshot lost: %s %v", name, err)
|
|
}
|
|
}
|
|
data, err := os.ReadFile(filepath.Join(home, ".credentials.yaml"))
|
|
if err != nil || string(data) != "live" {
|
|
t.Fatalf("live home changed: %q %v", data, err)
|
|
}
|
|
}
|
|
if err := os.Remove(filepath.Join(home, "dangling")); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if w = configCall(a, i, "POST", "restore", body); w.Code != 200 {
|
|
t.Fatal(w.Code, w.Body.String())
|
|
}
|
|
entries, err := os.ReadDir(base)
|
|
if err != nil || len(entries) != 4 {
|
|
t.Fatalf("completed snapshot not retained: %v %v", entries, err)
|
|
}
|
|
if w = configCall(a, i, "POST", "restore", body); w.Code != 409 {
|
|
t.Fatal("retention not enforced", w.Code, w.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestWorkspaceLeaseBlocksConfigAndUpgrade(t *testing.T) {
|
|
a, i, _ := configTestAPI(t, false)
|
|
a.workspaceReaders = map[string]int{i.ID: 1}
|
|
for _, action := range []string{"profile", "restore", "backups"} {
|
|
w := configCall(a, i, "POST", action, `{}`)
|
|
if w.Code != 409 || !strings.Contains(w.Body.String(), "instance files busy") {
|
|
t.Fatal(w.Code, w.Body.String())
|
|
}
|
|
}
|
|
w := configCall(a, i, "GET", "backups", "")
|
|
if w.Code != 200 {
|
|
t.Fatal(w.Code, w.Body.String())
|
|
}
|
|
w = httptest.NewRecorder()
|
|
body, _ := json.Marshal(map[string]string{"image_id": "sha256:" + strings.Repeat("a", 64), "password": "original-password"})
|
|
r := httptest.NewRequest("POST", "/", strings.NewReader(string(body)))
|
|
a.upgrade(w, r, i)
|
|
if w.Code != 409 || !strings.Contains(w.Body.String(), "instance files busy") {
|
|
t.Fatal(w.Code, w.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestUpgradeCopyMultipleDirectoryBatches(t *testing.T) {
|
|
if os.Geteuid() != 0 {
|
|
t.Skip("requires chown")
|
|
}
|
|
root, err := os.OpenRoot(t.TempDir())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer root.Close()
|
|
if err = root.Mkdir("home", 0700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for n := 0; n < 257; n++ {
|
|
configWrite(t, filepath.Join(root.Name(), "home", fmt.Sprint(n)), "x")
|
|
}
|
|
if err = upgradeCopy(context.Background(), root, "home", "snapshot", 257); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
entries, err := os.ReadDir(filepath.Join(root.Name(), "snapshot"))
|
|
if err != nil || len(entries) != 257 {
|
|
t.Fatal(len(entries), err)
|
|
}
|
|
}
|