Release URBM 2026.06.15.r005

This commit is contained in:
Mikei386
2026-06-15 07:04:41 +02:00
parent b93b06ec20
commit b03f168b78
10 changed files with 99 additions and 14 deletions
+16
View File
@@ -139,6 +139,13 @@ func (r *Runner) Forget(ctx context.Context, repo model.Repository, job model.Jo
if job.Retention.Monthly > 0 {
args = append(args, "--keep-monthly", fmt.Sprint(job.Retention.Monthly))
}
err := r.run(ctx, repo, args, nil, nil)
if err == nil || !isRepositoryLocked(err) {
return err
}
if unlockErr := r.Unlock(ctx, repo); unlockErr != nil {
return fmt.Errorf("retention repository locked; remove stale lock: %w", unlockErr)
}
return r.run(ctx, repo, args, nil, nil)
}
@@ -328,6 +335,15 @@ func resticError(message string, commandErr error) error {
}
}
func isRepositoryLocked(err error) bool {
if err == nil {
return false
}
lower := strings.ToLower(err.Error())
return strings.Contains(lower, "repository is already locked") ||
strings.Contains(lower, "unable to create lock in backend")
}
func repositoryLocation(repo model.Repository) string {
if repo.Type == model.RepositorySFTP && !strings.HasPrefix(repo.Location, "sftp:") {
return "sftp:" + repo.Location
+39
View File
@@ -76,6 +76,45 @@ func TestForgetUsesAgeAndCalendarRetention(t *testing.T) {
}
}
func TestForgetUnlocksStaleRepositoryLockAndRetries(t *testing.T) {
dir := t.TempDir()
logPath := filepath.Join(dir, "commands")
countPath := filepath.Join(dir, "forget-count")
script := filepath.Join(dir, "restic")
body := fmt.Sprintf(`#!/bin/sh
printf '%%s\n' "$*" >> '%s'
case " $* " in
*" forget "*)
count=0
[ -f '%s' ] && count=$(cat '%s')
count=$((count + 1))
printf '%%s' "$count" > '%s'
if [ "$count" -eq 1 ]; then
printf 'Fatal: unable to create lock in backend: repository is already locked\n' >&2
exit 11
fi
;;
esac
`, logPath, countPath, countPath, countPath)
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: "job", Retention: model.Retention{KeepWithinDays: 30}}
if err := runner.Forget(context.Background(), repo, job); err != nil {
t.Fatal(err)
}
commands, err := os.ReadFile(logPath)
if err != nil {
t.Fatal(err)
}
lines := strings.Split(strings.TrimSpace(string(commands)), "\n")
if len(lines) != 3 || !strings.Contains(lines[0], "forget") || !strings.Contains(lines[1], "unlock") || !strings.Contains(lines[2], "forget") {
t.Fatalf("expected forget, unlock, forget; got %q", lines)
}
}
func TestUnlockUsesSafeResticCommand(t *testing.T) {
dir := t.TempDir()
argsPath := filepath.Join(dir, "args")