1
0
Fork 0
mirror of https://github.com/restic/restic.git synced 2024-12-25 01:06:39 +00:00

Merge pull request #196 from restic/minor-refactor

A collection of minor refactorings from #179
This commit is contained in:
Alexander Neumann 2015-06-08 19:24:35 +02:00
commit c37ae44ff5
7 changed files with 138 additions and 82 deletions

View file

@ -18,10 +18,16 @@ type Cache struct {
base string base string
} }
func NewCache(repo *repository.Repository) (*Cache, error) { // NewCache returns a new cache at cacheDir. If it is the empty string, the
cacheDir, err := getCacheDir() // default cache location is chosen.
if err != nil { func NewCache(repo *repository.Repository, cacheDir string) (*Cache, error) {
return nil, err var err error
if cacheDir == "" {
cacheDir, err = getCacheDir()
if err != nil {
return nil, err
}
} }
basedir := filepath.Join(cacheDir, repo.Config.ID) basedir := filepath.Join(cacheDir, repo.Config.ID)

View file

@ -11,7 +11,7 @@ func TestCache(t *testing.T) {
repo := SetupRepo(t) repo := SetupRepo(t)
defer TeardownRepo(t, repo) defer TeardownRepo(t, repo)
_, err := restic.NewCache(repo) _, err := restic.NewCache(repo, "")
OK(t, err) OK(t, err)
arch := restic.NewArchiver(repo) arch := restic.NewArchiver(repo)

View file

@ -9,6 +9,7 @@ import (
"github.com/restic/restic" "github.com/restic/restic"
"github.com/restic/restic/backend" "github.com/restic/restic/backend"
"github.com/restic/restic/repository"
"golang.org/x/crypto/ssh/terminal" "golang.org/x/crypto/ssh/terminal"
) )
@ -97,7 +98,7 @@ func (cmd CmdBackup) Usage() string {
} }
func newScanProgress() *restic.Progress { func newScanProgress() *restic.Progress {
if !terminal.IsTerminal(int(os.Stdout.Fd())) { if disableProgress() {
return nil return nil
} }
@ -113,7 +114,7 @@ func newScanProgress() *restic.Progress {
} }
func newArchiveProgress(todo restic.Stat) *restic.Progress { func newArchiveProgress(todo restic.Stat) *restic.Progress {
if !terminal.IsTerminal(int(os.Stdout.Fd())) { if disableProgress() {
return nil return nil
} }
@ -162,6 +163,43 @@ func newArchiveProgress(todo restic.Stat) *restic.Progress {
return archiveProgress return archiveProgress
} }
func samePaths(expected, actual []string) bool {
if expected == nil || actual == nil {
return true
}
if len(expected) != len(actual) {
return false
}
for i := range expected {
if expected[i] != actual[i] {
return false
}
}
return true
}
func findLatestSnapshot(repo *repository.Repository, targets []string) (backend.ID, error) {
var (
latest time.Time
latestID backend.ID
)
for snapshotID := range repo.List(backend.Snapshot, make(chan struct{})) {
snapshot, err := restic.LoadSnapshot(repo, snapshotID)
if err != nil {
return nil, fmt.Errorf("Error listing snapshot: %v", err)
}
if snapshot.Time.After(latest) && samePaths(snapshot.Paths, targets) {
latest = snapshot.Time
latestID = snapshotID
}
}
return latestID, nil
}
func (cmd CmdBackup) Execute(args []string) error { func (cmd CmdBackup) Execute(args []string) error {
if len(args) == 0 { if len(args) == 0 {
return fmt.Errorf("wrong number of parameters, Usage: %s", cmd.Usage()) return fmt.Errorf("wrong number of parameters, Usage: %s", cmd.Usage())
@ -194,43 +232,22 @@ func (cmd CmdBackup) Execute(args []string) error {
return fmt.Errorf("invalid id %q: %v", cmd.Parent, err) return fmt.Errorf("invalid id %q: %v", cmd.Parent, err)
} }
fmt.Printf("found parent snapshot %v\n", parentSnapshotID) verbosePrintf("found parent snapshot %v\n", parentSnapshotID.Str())
} }
// Find last snapshot to set it as parent, if not already set // Find last snapshot to set it as parent, if not already set
if !cmd.Force && parentSnapshotID == nil { if !cmd.Force && parentSnapshotID == nil {
samePaths := func(expected, actual []string) bool { parentSnapshotID, err = findLatestSnapshot(s, target)
if len(expected) != len(actual) { if err != nil {
return false return err
}
for i := range expected {
if expected[i] != actual[i] {
return false
}
}
return true
}
var latest time.Time
for id := range s.List(backend.Snapshot, make(chan struct{})) {
snapshot, err := restic.LoadSnapshot(s, id)
if err != nil {
return fmt.Errorf("Error listing snapshot: %v", err)
}
if snapshot.Time.After(latest) && samePaths(snapshot.Paths, target) {
latest = snapshot.Time
parentSnapshotID = id
}
} }
if parentSnapshotID != nil { if parentSnapshotID != nil {
fmt.Printf("using parent snapshot %v\n", parentSnapshotID) verbosePrintf("using parent snapshot %v\n", parentSnapshotID)
} }
} }
fmt.Printf("scan %v\n", target) verbosePrintf("scan %v\n", target)
stat, err := restic.Scan(target, newScanProgress()) stat, err := restic.Scan(target, newScanProgress())
@ -252,12 +269,7 @@ func (cmd CmdBackup) Execute(args []string) error {
return err return err
} }
plen, err := s.PrefixLength(backend.Snapshot) verbosePrintf("snapshot %s saved\n", id.Str())
if err != nil {
return err
}
fmt.Printf("snapshot %s saved\n", id[:plen/2])
return nil return nil
} }

View file

@ -32,7 +32,7 @@ func (cmd CmdCache) Execute(args []string) error {
return err return err
} }
cache, err := restic.NewCache(s) cache, err := restic.NewCache(s, opts.CacheDir)
if err != nil { if err != nil {
return err return err
} }

View file

@ -56,15 +56,28 @@ func listKeys(s *repository.Repository) error {
return nil return nil
} }
func addKey(s *repository.Repository) error { func getNewPassword() (string, error) {
pw := readPassword("RESTIC_NEWPASSWORD", "enter password for new key: ") newPassword := os.Getenv("RESTIC_NEWPASSWORD")
pw2 := readPassword("RESTIC_NEWPASSWORD", "enter password again: ")
if pw != pw2 { if newPassword == "" {
return errors.New("passwords do not match") newPassword = readPassword("enter password for new key: ")
newPassword2 := readPassword("enter password again: ")
if newPassword != newPassword2 {
return "", errors.New("passwords do not match")
}
} }
id, err := repository.AddKey(s, pw, s.Key()) return newPassword, nil
}
func addKey(repo *repository.Repository) error {
newPassword, err := getNewPassword()
if err != nil {
return err
}
id, err := repository.AddKey(repo, newPassword, repo.Key())
if err != nil { if err != nil {
return fmt.Errorf("creating new key failed: %v\n", err) return fmt.Errorf("creating new key failed: %v\n", err)
} }
@ -88,22 +101,18 @@ func deleteKey(repo *repository.Repository, name string) error {
return nil return nil
} }
func changePassword(s *repository.Repository) error { func changePassword(repo *repository.Repository) error {
pw := readPassword("RESTIC_NEWPASSWORD", "enter password for new key: ") newPassword, err := getNewPassword()
pw2 := readPassword("RESTIC_NEWPASSWORD", "enter password again: ") if err != nil {
return err
if pw != pw2 {
return errors.New("passwords do not match")
} }
// add new key id, err := repository.AddKey(repo, newPassword, repo.Key())
id, err := repository.AddKey(s, pw, s.Key())
if err != nil { if err != nil {
return fmt.Errorf("creating new key failed: %v\n", err) return fmt.Errorf("creating new key failed: %v\n", err)
} }
// remove old key err = repo.Backend().Remove(backend.Key, repo.KeyName())
err = s.Backend().Remove(backend.Key, s.KeyName())
if err != nil { if err != nil {
return err return err
} }

View file

@ -20,7 +20,11 @@ import (
var version = "compiled manually" var version = "compiled manually"
var opts struct { var opts struct {
Repo string `short:"r" long:"repo" description:"Repository directory to backup to/restore from"` Repo string `short:"r" long:"repo" description:"Repository directory to backup to/restore from"`
CacheDir string ` long:"cache-dir" description:"Directory to use as a local cache"`
Quiet bool `short:"q" long:"quiet" default:"false" description:"Do not output comprehensive progress report"`
password string
} }
var parser = flags.NewParser(&opts, flags.Default) var parser = flags.NewParser(&opts, flags.Default)
@ -33,15 +37,7 @@ func errx(code int, format string, data ...interface{}) {
os.Exit(code) os.Exit(code)
} }
func readPassword(env string, prompt string) string { func readPassword(prompt string) string {
if env != "" {
p := os.Getenv(env)
if p != "" {
return p
}
}
fmt.Fprint(os.Stderr, prompt) fmt.Fprint(os.Stderr, prompt)
pw, err := terminal.ReadPassword(int(os.Stdin.Fd())) pw, err := terminal.ReadPassword(int(os.Stdin.Fd()))
if err != nil { if err != nil {
@ -52,6 +48,34 @@ func readPassword(env string, prompt string) string {
return string(pw) return string(pw)
} }
func disableProgress() bool {
if opts.Quiet {
return true
}
if !terminal.IsTerminal(int(os.Stdout.Fd())) {
return true
}
return false
}
func silenceRequested() bool {
if opts.Quiet {
return true
}
return false
}
func verbosePrintf(format string, args ...interface{}) {
if silenceRequested() {
return
}
fmt.Printf(format, args...)
}
type CmdInit struct{} type CmdInit struct{}
func (cmd CmdInit) Execute(args []string) error { func (cmd CmdInit) Execute(args []string) error {
@ -59,11 +83,15 @@ func (cmd CmdInit) Execute(args []string) error {
return errors.New("Please specify repository location (-r)") return errors.New("Please specify repository location (-r)")
} }
pw := readPassword("RESTIC_PASSWORD", "enter password for new backend: ") if opts.password == "" {
pw2 := readPassword("RESTIC_PASSWORD", "enter password again: ") pw := readPassword("enter password for new backend: ")
pw2 := readPassword("enter password again: ")
if pw != pw2 { if pw != pw2 {
errx(1, "passwords do not match") errx(1, "passwords do not match")
}
opts.password = pw
} }
be, err := create(opts.Repo) be, err := create(opts.Repo)
@ -73,16 +101,17 @@ func (cmd CmdInit) Execute(args []string) error {
} }
s := repository.New(be) s := repository.New(be)
err = s.Init(pw) err = s.Init(opts.password)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "creating key in backend at %s failed: %v\n", opts.Repo, err) fmt.Fprintf(os.Stderr, "creating key in backend at %s failed: %v\n", opts.Repo, err)
os.Exit(1) os.Exit(1)
} }
fmt.Printf("created restic backend %v at %s\n", s.Config.ID[:10], opts.Repo) verbosePrintf("created restic backend %v at %s\n", s.Config.ID[:10], opts.Repo)
verbosePrintf("\n")
fmt.Println("Please note that knowledge of your password is required to access the repository.") verbosePrintf("Please note that knowledge of your password is required to access\n")
fmt.Println("Losing your password means that your data is irrecoverably lost.") verbosePrintf("the repository. Losing your password means that your data is\n")
verbosePrintf("irrecoverably lost.\n")
return nil return nil
} }
@ -145,7 +174,11 @@ func OpenRepo() (*repository.Repository, error) {
s := repository.New(be) s := repository.New(be)
err = s.SearchKey(readPassword("RESTIC_PASSWORD", "enter password for repository: ")) if opts.password == "" {
opts.password = readPassword("enter password for repository: ")
}
err = s.SearchKey(opts.password)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to open repo: %v", err) return nil, fmt.Errorf("unable to open repo: %v", err)
} }
@ -170,6 +203,7 @@ func main() {
// defer profile.Start(profile.MemProfileRate(100000), profile.ProfilePath(".")).Stop() // defer profile.Start(profile.MemProfileRate(100000), profile.ProfilePath(".")).Stop()
// defer profile.Start(profile.CPUProfile, profile.ProfilePath(".")).Stop() // defer profile.Start(profile.CPUProfile, profile.ProfilePath(".")).Stop()
opts.Repo = os.Getenv("RESTIC_REPOSITORY") opts.Repo = os.Getenv("RESTIC_REPOSITORY")
opts.password = os.Getenv("RESTIC_PASSWORD")
debug.Log("restic", "main %#v", os.Args) debug.Log("restic", "main %#v", os.Args)

View file

@ -3,7 +3,6 @@ package test_helper
import ( import (
"flag" "flag"
"io/ioutil" "io/ioutil"
"os"
"path/filepath" "path/filepath"
"testing" "testing"
@ -25,10 +24,6 @@ func SetupRepo(t testing.TB) *repository.Repository {
b, err := local.Create(filepath.Join(tempdir, "repo")) b, err := local.Create(filepath.Join(tempdir, "repo"))
OK(t, err) OK(t, err)
// set cache dir below temp dir
err = os.Setenv("RESTIC_CACHE", filepath.Join(tempdir, "cache"))
OK(t, err)
repo := repository.New(b) repo := repository.New(b)
OK(t, repo.Init(*TestPassword)) OK(t, repo.Init(*TestPassword))
return repo return repo