feat: schedule persistent archive jobs with console management
This commit is contained in:
96
internal/retention/runtime_test.go
Normal file
96
internal/retention/runtime_test.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package retention
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
core "git.yuntuo.hk/k2safe/orbit/model"
|
||||
)
|
||||
|
||||
type idleArchiveStore struct {
|
||||
RuntimeStore
|
||||
scheduled int
|
||||
}
|
||||
|
||||
func (s *idleArchiveStore) ScheduleArchive(context.Context, core.ID) error { s.scheduled++; return nil }
|
||||
func (s *idleArchiveStore) ClaimArchive(context.Context, core.ID) (*ManagedJob, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func TestArchiveVolumeIdentityLockAndInitializationRecovery(t *testing.T) {
|
||||
store := &idleArchiveStore{}
|
||||
id, _ := core.NewID()
|
||||
runtime := &ArchiveRuntime{Directory: filepath.Join(t.TempDir(), "volume"), StorageID: id, MaxBytes: 8 << 20, Store: store}
|
||||
if err := runtime.Init(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
root, lock, err := openJob(runtime.Directory, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// 其他节点正在执行时,新节点仍可读取不可变卷身份并正常启动。
|
||||
if err = runtime.Init(); err != nil {
|
||||
t.Fatal("another node could not initialize", err)
|
||||
}
|
||||
other := *runtime
|
||||
other.StorageID, _ = core.NewID()
|
||||
if err = other.Init(); err == nil {
|
||||
t.Fatal("wrong volume accepted while busy")
|
||||
}
|
||||
if err = runtime.Tick(context.Background()); err != nil || store.scheduled != 0 {
|
||||
t.Fatal("busy volume scheduled work", err)
|
||||
}
|
||||
lock.Close()
|
||||
root.Close()
|
||||
if err = runtime.Tick(context.Background()); err != nil || store.scheduled != 1 {
|
||||
t.Fatal("unlocked volume did not schedule", err)
|
||||
}
|
||||
if err = other.Init(); err == nil {
|
||||
t.Fatal("wrong volume accepted")
|
||||
}
|
||||
// 首次身份文件原子发布前崩溃,只允许丢弃本工具的安全临时文件。
|
||||
fresh := *runtime
|
||||
fresh.Directory = filepath.Join(t.TempDir(), "interrupted")
|
||||
if err = os.Mkdir(fresh.Directory, 0700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = os.WriteFile(filepath.Join(fresh.Directory, ".write.tmp"), []byte(id), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = fresh.Init(); err != nil {
|
||||
t.Fatal("volume init did not recover", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestArchiveVolumeScanCountsAllJobsAndRejectsLinks(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
job := "job"
|
||||
if err := os.Mkdir(filepath.Join(dir, job), 0700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(dir, "other"), []byte("volume"), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(dir, job, "batch"), []byte("job"), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
root, err := os.OpenRoot(dir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer root.Close()
|
||||
count := 0
|
||||
total, current, err := archiveVolumeUsage(context.Background(), root, ".", job, &count)
|
||||
if err != nil || total != 9 || current != 3 || count != 3 {
|
||||
t.Fatal(total, current, count, err)
|
||||
}
|
||||
if err = os.Symlink("other", filepath.Join(dir, "unexpected")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
count = 0
|
||||
if _, _, err = archiveVolumeUsage(context.Background(), root, ".", job, &count); err == nil {
|
||||
t.Fatal("volume symlink accepted")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user