Один бинарник backupctl: - server: API для агентов и restore, веб-морда (один админ), SQLite, ретеншн GFS, heavy-части, уведомления ntfy, раздача agent.sh/restore.sh; - backup/plan: сбор по каталогу категорий (files/systemd/xui/compose), горячие дампы Postgres/Mongo/MySQL, снимок x-ui.db, docker-тома, digest образов, проверка crontab, потоковая отправка tar.zst; - restore: провижининг на основе vps_installer с теми же версиями пакетов, recipes 3x-ui/nebula/sing-box нужных версий, восстановление compose с заливкой дампов и закреплением образов по digest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
52 lines
1.7 KiB
Go
52 lines
1.7 KiB
Go
package restore
|
|
|
|
import (
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"gitea.hsrv.site/mrleo1nid/backup-master/internal/manifest"
|
|
)
|
|
|
|
func TestOrder(t *testing.T) {
|
|
cats := []manifest.Category{
|
|
{Name: "system"},
|
|
{Name: "stoat-mbot", RestoreAfter: []string{"stoat", "psiphon"}},
|
|
{Name: "pnvpanel"},
|
|
{Name: "stoat"},
|
|
}
|
|
var names []string
|
|
for _, c := range order(cats) {
|
|
names = append(names, c.Name)
|
|
}
|
|
want := []string{"system", "pnvpanel", "stoat", "stoat-mbot"}
|
|
for i := range want {
|
|
if names[i] != want[i] {
|
|
t.Fatalf("order = %v, want %v", names, want)
|
|
}
|
|
}
|
|
// цикл не зависает
|
|
cyc := order([]manifest.Category{{Name: "a", RestoreAfter: []string{"b"}}, {Name: "b", RestoreAfter: []string{"a"}}})
|
|
if len(cyc) != 2 {
|
|
t.Fatal("цикл потерял категории")
|
|
}
|
|
}
|
|
|
|
func TestRoute(t *testing.T) {
|
|
r := &restorer{staging: "/stage", volumes: map[string]string{"pnvpanel_ticket_uploads": "/var/lib/docker/volumes/pnvpanel_ticket_uploads/_data"}}
|
|
cases := map[string]string{
|
|
"pnvpanel/files/opt/PnvPanel/.env": "/opt/PnvPanel/.env",
|
|
"pnvpanel/dumps/db.pgdump": filepath.Join("/stage", "pnvpanel", "dumps", "db.pgdump"),
|
|
"pnvpanel/volumes/pnvpanel_ticket_uploads/a/b.png": filepath.Join("/var/lib/docker/volumes/pnvpanel_ticket_uploads/_data", "a", "b.png"),
|
|
"xui/bin/usr/local/x-ui/x-ui": filepath.Join("/stage", "xui", "bin", "usr", "local", "x-ui", "x-ui"),
|
|
}
|
|
for name, want := range cases {
|
|
got, ok := r.route(name)
|
|
if !ok || got != want {
|
|
t.Errorf("route(%q) = %q, %v; want %q", name, got, ok, want)
|
|
}
|
|
}
|
|
if _, ok := r.route("manifest.json"); ok {
|
|
t.Error("manifest.json не должен распаковываться")
|
|
}
|
|
}
|