Release URBM 2026.06.15.r007
This commit is contained in:
@@ -125,6 +125,28 @@ func (r *Runner) Backup(ctx context.Context, repo model.Repository, job model.Jo
|
||||
return summary, err
|
||||
}
|
||||
|
||||
func (r *Runner) BackupImage(ctx context.Context, repo model.Repository, job model.Job, image io.Reader, progress ProgressCallback) (Summary, error) {
|
||||
args := []string{"backup", "--json", "--stdin", "--stdin-filename", "urbm-usb-flash.img", "--compression", job.Compression}
|
||||
for _, tag := range append([]string{"urbm", "job:" + job.ID, "usb-image"}, job.Tags...) {
|
||||
args = append(args, "--tag", tag)
|
||||
}
|
||||
var summary Summary
|
||||
err := r.runWithInput(ctx, repo, args, func(line []byte) {
|
||||
var status Progress
|
||||
if json.Unmarshal(line, &status) == nil && status.MessageType == "status" {
|
||||
if progress != nil {
|
||||
progress(status)
|
||||
}
|
||||
return
|
||||
}
|
||||
var candidate Summary
|
||||
if json.Unmarshal(line, &candidate) == nil && candidate.MessageType == "summary" {
|
||||
summary = candidate
|
||||
}
|
||||
}, nil, image)
|
||||
return summary, err
|
||||
}
|
||||
|
||||
func (r *Runner) Forget(ctx context.Context, repo model.Repository, job model.Job) error {
|
||||
args := []string{"forget", "--tag", "job:" + job.ID}
|
||||
if job.Retention.KeepWithinDays > 0 {
|
||||
@@ -201,6 +223,10 @@ func (r *Runner) Restore(ctx context.Context, repo model.Repository, task model.
|
||||
}
|
||||
|
||||
func (r *Runner) run(ctx context.Context, repo model.Repository, args []string, onLine func([]byte), capture *[]byte) error {
|
||||
return r.runWithInput(ctx, repo, args, onLine, capture, nil)
|
||||
}
|
||||
|
||||
func (r *Runner) runWithInput(ctx context.Context, repo model.Repository, args []string, onLine func([]byte), capture *[]byte, input io.Reader) error {
|
||||
password, err := r.Secrets.Get(repo.PasswordRef)
|
||||
if err != nil {
|
||||
return fmt.Errorf("authentication: load repository password: %w", err)
|
||||
@@ -257,6 +283,7 @@ func (r *Runner) run(ctx context.Context, repo model.Repository, args []string,
|
||||
}
|
||||
fullArgs := append(globalArgs, args...)
|
||||
cmd := exec.CommandContext(ctx, r.Binary, fullArgs...)
|
||||
cmd.Stdin = input
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
||||
cmd.Cancel = func() error {
|
||||
if cmd.Process == nil {
|
||||
|
||||
@@ -51,6 +51,37 @@ func TestBackupUsesPasswordFileAndStructuredArguments(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackupImageStreamsRawDeviceWithStableFilename(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
argsPath := filepath.Join(dir, "args")
|
||||
imagePath := filepath.Join(dir, "image")
|
||||
script := filepath.Join(dir, "restic")
|
||||
body := fmt.Sprintf("#!/bin/sh\nprintf '%%s\\n' \"$@\" > '%s'\ncat > '%s'\nprintf '%%s\\n' '{\"message_type\":\"summary\",\"snapshot_id\":\"image123\",\"data_added\":4,\"total_bytes_processed\":4,\"total_files_processed\":1}'\n", argsPath, imagePath)
|
||||
if err := os.WriteFile(script, []byte(body), 0700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runner := &Runner{Binary: script, RuntimeDir: dir, Secrets: fakeSecrets{"password": "secret"}}
|
||||
repo := model.Repository{Type: model.RepositoryLocal, Location: "/repo", PasswordRef: "password"}
|
||||
job := model.Job{ID: "flash", Compression: "auto"}
|
||||
summary, err := runner.BackupImage(context.Background(), repo, job, strings.NewReader("disk"), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if summary.SnapshotID != "image123" || summary.TotalBytesProcessed != 4 {
|
||||
t.Fatalf("unexpected summary: %+v", summary)
|
||||
}
|
||||
args, _ := os.ReadFile(argsPath)
|
||||
for _, expected := range []string{"--stdin", "--stdin-filename", "urbm-usb-flash.img", "usb-image"} {
|
||||
if !strings.Contains(string(args), expected) {
|
||||
t.Fatalf("arguments missing %q: %s", expected, args)
|
||||
}
|
||||
}
|
||||
image, _ := os.ReadFile(imagePath)
|
||||
if string(image) != "disk" {
|
||||
t.Fatalf("streamed image = %q", image)
|
||||
}
|
||||
}
|
||||
|
||||
func TestForgetUsesAgeAndCalendarRetention(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
argsPath := filepath.Join(dir, "args")
|
||||
|
||||
Reference in New Issue
Block a user