Support attaching existing Restic repositories
This commit is contained in:
@@ -414,7 +414,7 @@ func resticError(message string, commandErr error) error {
|
||||
case strings.Contains(lower, "wrong password") || strings.Contains(lower, "no key found"):
|
||||
return errors.New("authentication: wrong repository password. Enter the original Restic password under Edit and select Test")
|
||||
case strings.Contains(lower, "repository does not exist") || strings.Contains(lower, "is there a repository at the following location?"):
|
||||
return errors.New("repository: destination is not initialized; open Repositories and select Initialize once")
|
||||
return errors.New("repository: Am konfigurierten Ziel wurde keine Restic-config gefunden. Für ein vorhandenes Repository den exakten Repository-Ordner eintragen; nur ein wirklich neues, leeres Ziel initialisieren")
|
||||
default:
|
||||
message = strings.TrimSpace(strings.TrimPrefix(message, "Fatal:"))
|
||||
return fmt.Errorf("restic: %s: %w", message, commandErr)
|
||||
|
||||
@@ -298,6 +298,13 @@ func TestResticErrorExplainsCiphertextVerification(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestResticErrorExplainsMissingExistingRepositoryPath(t *testing.T) {
|
||||
err := resticError("Fatal: repository does not exist", fmt.Errorf("exit status 10"))
|
||||
if !strings.Contains(err.Error(), "exakten Repository-Ordner") || !strings.Contains(err.Error(), "wirklich neues") {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunnerPausesResumesAndCancelsProcessGroup(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
ticksPath := filepath.Join(dir, "ticks")
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestValidateExistingRepositoryPathAcceptsRepositoryRoot(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
if err := os.WriteFile(filepath.Join(root, "config"), []byte("restic"), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := validateExistingRepositoryPath(root); err != nil {
|
||||
t.Fatalf("repository root rejected: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateExistingRepositoryPathSuggestsDirectChild(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
repository := filepath.Join(root, "server-backup")
|
||||
if err := os.Mkdir(repository, 0700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(repository, "config"), []byte("restic"), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err := validateExistingRepositoryPath(root)
|
||||
if err == nil || !strings.Contains(err.Error(), repository) {
|
||||
t.Fatalf("missing repository suggestion: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateExistingRepositoryPathExplainsMissingConfig(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
err := validateExistingRepositoryPath(root)
|
||||
if err == nil || !strings.Contains(err.Error(), "config, data, index, keys und snapshots") {
|
||||
t.Fatalf("missing layout guidance: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -262,10 +262,43 @@ func (s *Service) TestRepository(ctx context.Context, repoID string, initialize
|
||||
if initialize {
|
||||
return s.restic.Init(ctx, mounted.Repository)
|
||||
}
|
||||
if mounted.Repository.Type != model.RepositorySFTP {
|
||||
if err := validateExistingRepositoryPath(mounted.Repository.Location); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
_, err = s.restic.Snapshots(ctx, mounted.Repository)
|
||||
return err
|
||||
}
|
||||
|
||||
func validateExistingRepositoryPath(location string) error {
|
||||
configPath := filepath.Join(location, "config")
|
||||
if info, err := os.Stat(configPath); err == nil && !info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
entries, readErr := os.ReadDir(location)
|
||||
if readErr != nil {
|
||||
return fmt.Errorf("repository: Repository-Ordner %q kann nicht gelesen werden: %w", location, readErr)
|
||||
}
|
||||
candidates := make([]string, 0, 3)
|
||||
for _, entry := range entries {
|
||||
if !entry.IsDir() {
|
||||
continue
|
||||
}
|
||||
candidate := filepath.Join(location, entry.Name())
|
||||
if info, err := os.Stat(filepath.Join(candidate, "config")); err == nil && !info.IsDir() {
|
||||
candidates = append(candidates, candidate)
|
||||
if len(candidates) == 3 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(candidates) > 0 {
|
||||
return fmt.Errorf("repository: Im gewählten Ordner liegt keine Restic-config. Verwende direkt den Repository-Ordner: %s", strings.Join(candidates, ", "))
|
||||
}
|
||||
return fmt.Errorf("repository: Im gewählten Ordner %q wurde keine Restic-config gefunden. Ein vorhandenes Repository muss direkt auf den Ordner zeigen, der config, data, index, keys und snapshots enthält", location)
|
||||
}
|
||||
|
||||
func (s *Service) UnlockRepository(ctx context.Context, repoID string) error {
|
||||
repo, ok := s.repository(repoID)
|
||||
if !ok {
|
||||
|
||||
Reference in New Issue
Block a user