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:
commit
c37ae44ff5
7 changed files with 138 additions and 82 deletions
14
cache.go
14
cache.go
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue