Refactor commands

This commit is contained in:
Alexander Neumann 2014-12-07 16:30:52 +01:00
parent b3deca33a9
commit 28bb061ad3
10 changed files with 276 additions and 94 deletions

View File

@ -1,7 +1,6 @@
package main package main
import ( import (
"errors"
"fmt" "fmt"
"os" "os"
"strings" "strings"
@ -12,8 +11,16 @@ import (
"golang.org/x/crypto/ssh/terminal" "golang.org/x/crypto/ssh/terminal"
) )
type CmdBackup struct{}
func init() { func init() {
commands["backup"] = commandBackup _, err := parser.AddCommand("backup",
"save file/directory",
"The backup command creates a snapshot of a file or directory",
&CmdBackup{})
if err != nil {
panic(err)
}
} }
func format_bytes(c uint64) string { func format_bytes(c uint64) string {
@ -56,13 +63,21 @@ func print_tree2(indent int, t *restic.Tree) {
} }
} }
func commandBackup(be backend.Server, key *restic.Key, args []string) error { func (cmd CmdBackup) Usage() string {
if len(args) < 1 || len(args) > 2 { return "DIR/FILE [snapshot-ID]"
return errors.New("usage: backup [dir|file] [snapshot-id]") }
func (cmd CmdBackup) Execute(args []string) error {
if len(args) == 0 || len(args) > 2 {
return fmt.Errorf("wrong number of parameters, Usage: %s", cmd.Usage())
}
be, key, err := OpenRepo()
if err != nil {
return err
} }
var parentSnapshotID backend.ID var parentSnapshotID backend.ID
var err error
target := args[0] target := args[0]
if len(args) > 1 { if len(args) > 1 {

View File

@ -10,13 +10,30 @@ import (
"github.com/restic/restic/backend" "github.com/restic/restic/backend"
) )
type CmdCat struct{}
func init() { func init() {
commands["cat"] = commandCat _, err := parser.AddCommand("cat",
"dump something",
"The cat command dumps data structures or data from a repository",
&CmdCat{})
if err != nil {
panic(err)
}
} }
func commandCat(be backend.Server, key *restic.Key, args []string) error { func (cmd CmdCat) Usage() string {
return "[blob|tree|snapshot|key|lock] ID"
}
func (cmd CmdCat) Execute(args []string) error {
if len(args) != 2 { if len(args) != 2 {
return errors.New("usage: cat [blob|tree|snapshot|key|lock] ID") return fmt.Errorf("type or ID not specified, Usage: %s", cmd.Usage())
}
be, key, err := OpenRepo()
if err != nil {
return err
} }
tpe := args[0] tpe := args[0]

View File

@ -1,16 +1,18 @@
package main package main
import ( import (
"errors"
"fmt" "fmt"
"path/filepath" "path/filepath"
"time"
"github.com/restic/restic" "github.com/restic/restic"
"github.com/restic/restic/backend" "github.com/restic/restic/backend"
) )
func init() { type findQuery struct {
commands["find"] = commandFind name string
minModTime time.Time
maxModTime time.Time
} }
type findResult struct { type findResult struct {
@ -18,6 +20,21 @@ type findResult struct {
path string path string
} }
type CmdFind struct {
Oldest time.Time `short:"o" long:"oldest" description:"Oldest modification date/time"`
Newest time.Time `short:"n" long:"newest" description:"Newest modification date/time"`
}
func init() {
_, err := parser.AddCommand("find",
"find a file/directory",
"The find command searches for files or directories in snapshots",
&CmdFind{})
if err != nil {
panic(err)
}
}
func findInTree(ch *restic.ContentHandler, id backend.ID, path, pattern string) ([]findResult, error) { func findInTree(ch *restic.ContentHandler, id backend.ID, path, pattern string) ([]findResult, error) {
debug("checking tree %v\n", id) debug("checking tree %v\n", id)
@ -83,9 +100,18 @@ func findInSnapshot(be backend.Server, key *restic.Key, id backend.ID, pattern s
return nil return nil
} }
func commandFind(be backend.Server, key *restic.Key, args []string) error { func (cmd CmdFind) Usage() string {
return "[find-OPTIONS] PATTERN [snapshot-ID]"
}
func (cmd CmdFind) Execute(args []string) error {
if len(args) == 0 { if len(args) == 0 {
return errors.New("usage: find PATTERN [snapshot-id]") return fmt.Errorf("no pattern given, Usage: %s", cmd.Usage())
}
be, key, err := OpenRepo()
if err != nil {
return err
} }
pattern := args[0] pattern := args[0]

View File

@ -1,15 +1,22 @@
package main package main
import ( import (
"errors"
"fmt" "fmt"
"github.com/restic/restic" "github.com/restic/restic"
"github.com/restic/restic/backend" "github.com/restic/restic/backend"
) )
type CmdFsck struct{}
func init() { func init() {
commands["fsck"] = commandFsck _, err := parser.AddCommand("fsck",
"check the repository",
"The fsck command check the integrity and consistency of the repository",
&CmdFsck{})
if err != nil {
panic(err)
}
} }
func fsckFile(ch *restic.ContentHandler, IDs []backend.ID) error { func fsckFile(ch *restic.ContentHandler, IDs []backend.ID) error {
@ -92,9 +99,18 @@ func fsck_snapshot(be backend.Server, key *restic.Key, id backend.ID) error {
return fsckTree(ch, sn.Tree) return fsckTree(ch, sn.Tree)
} }
func commandFsck(be backend.Server, key *restic.Key, args []string) error { func (cmd CmdFsck) Usage() string {
return "fsck [all|snapshot-ID]"
}
func (cmd CmdFsck) Execute(args []string) error {
if len(args) == 0 { if len(args) == 0 {
return errors.New("usage: fsck [all|snapshot-id]") return fmt.Errorf("type or ID not specified, Usage: %s", cmd.Usage())
}
be, key, err := OpenRepo()
if err != nil {
return err
} }
if len(args) == 1 && args[0] != "all" { if len(args) == 1 && args[0] != "all" {

View File

@ -10,8 +10,16 @@ import (
"github.com/restic/restic/backend" "github.com/restic/restic/backend"
) )
type CmdKey struct{}
func init() { func init() {
commands["key"] = commandKey _, err := parser.AddCommand("key",
"manage keys",
"The key command manages keys (passwords) of a repository",
&CmdKey{})
if err != nil {
panic(err)
}
} }
func list_keys(be backend.Server, key *restic.Key) error { func list_keys(be backend.Server, key *restic.Key) error {
@ -103,9 +111,18 @@ func change_password(be backend.Server, key *restic.Key) error {
return nil return nil
} }
func commandKey(be backend.Server, key *restic.Key, args []string) error { func (cmd CmdKey) Usage() string {
return "[list|add|rm|change] [ID]"
}
func (cmd CmdKey) Execute(args []string) error {
if len(args) < 1 || (args[0] == "rm" && len(args) != 2) { if len(args) < 1 || (args[0] == "rm" && len(args) != 2) {
return errors.New("usage: key [list|add|rm|change] [ID]") return fmt.Errorf("wrong number of arguments, Usage: %s", cmd.Usage())
}
be, key, err := OpenRepo()
if err != nil {
return err
} }
switch args[0] { switch args[0] {

View File

@ -3,18 +3,33 @@ package main
import ( import (
"errors" "errors"
"fmt" "fmt"
"github.com/restic/restic"
"github.com/restic/restic/backend" "github.com/restic/restic/backend"
) )
type CmdList struct{}
func init() { func init() {
commands["list"] = commandList _, err := parser.AddCommand("list",
"lists data",
"The list command lists structures or data of a repository",
&CmdList{})
if err != nil {
panic(err)
}
} }
func commandList(be backend.Server, key *restic.Key, args []string) error { func (cmd CmdList) Usage() string {
return "[data|trees|snapshots|keys|locks]"
}
func (cmd CmdList) Execute(args []string) error {
if len(args) != 1 { if len(args) != 1 {
return errors.New("usage: list [data|trees|snapshots|keys|locks]") return fmt.Errorf("type not specified, Usage: %s", cmd.Usage())
}
be, key, err := OpenRepo()
if err != nil {
return err
} }
var ( var (

View File

@ -1,7 +1,6 @@
package main package main
import ( import (
"errors"
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
@ -10,8 +9,16 @@ import (
"github.com/restic/restic/backend" "github.com/restic/restic/backend"
) )
type CmdLs struct{}
func init() { func init() {
commands["ls"] = commandLs _, err := parser.AddCommand("ls",
"list files",
"The ls command lists all files and directories in a snapshot",
&CmdLs{})
if err != nil {
panic(err)
}
} }
func print_node(prefix string, n *restic.Node) string { func print_node(prefix string, n *restic.Node) string {
@ -52,9 +59,18 @@ func print_tree(prefix string, ch *restic.ContentHandler, id backend.ID) error {
return nil return nil
} }
func commandLs(be backend.Server, key *restic.Key, args []string) error { func (cmd CmdLs) Usage() string {
return "ls snapshot-ID [DIR]"
}
func (cmd CmdLs) Execute(be backend.Server, key *restic.Key, args []string) error {
if len(args) < 1 || len(args) > 2 { if len(args) < 1 || len(args) > 2 {
return errors.New("usage: ls SNAPSHOT_ID [dir]") return fmt.Errorf("wrong number of arguments, Usage: %s", cmd.Usage())
}
be, key, err := OpenRepo()
if err != nil {
return err
} }
id, err := backend.FindSnapshot(be, args[0]) id, err := backend.FindSnapshot(be, args[0])

View File

@ -1,7 +1,6 @@
package main package main
import ( import (
"errors"
"fmt" "fmt"
"os" "os"
@ -9,13 +8,30 @@ import (
"github.com/restic/restic/backend" "github.com/restic/restic/backend"
) )
type CmdRestore struct{}
func init() { func init() {
commands["restore"] = commandRestore _, err := parser.AddCommand("restore",
"restore a snapshot",
"The restore command restores a snapshot to a directory",
&CmdRestore{})
if err != nil {
panic(err)
}
} }
func commandRestore(be backend.Server, key *restic.Key, args []string) error { func (cmd CmdRestore) Usage() string {
return "snapshot-ID TARGETDIR"
}
func (cmd CmdRestore) Execute(args []string) error {
if len(args) != 2 { if len(args) != 2 {
return errors.New("usage: restore ID dir") return fmt.Errorf("wrong number of arguments, Usage: %s", cmd.Usage())
}
be, key, err := OpenRepo()
if err != nil {
return err
} }
id, err := backend.FindSnapshot(be, args[0]) id, err := backend.FindSnapshot(be, args[0])

View File

@ -1,7 +1,6 @@
package main package main
import ( import (
"errors"
"fmt" "fmt"
"io" "io"
"os" "os"
@ -72,13 +71,30 @@ func reltime(t time.Time) string {
} }
} }
type CmdSnapshots struct{}
func init() { func init() {
commands["snapshots"] = commandSnapshots _, err := parser.AddCommand("snapshots",
"show snapshots",
"The snapshots command lists all snapshots stored in a repository",
&CmdSnapshots{})
if err != nil {
panic(err)
}
} }
func commandSnapshots(be backend.Server, key *restic.Key, args []string) error { func (cmd CmdSnapshots) Usage() string {
return ""
}
func (cmd CmdSnapshots) Execute(args []string) error {
if len(args) != 0 { if len(args) != 0 {
return errors.New("usage: snapshots") return fmt.Errorf("wrong number of arguments, usage: %s", cmd.Usage())
}
be, key, err := OpenRepo()
if err != nil {
return err
} }
ch, err := restic.NewContentHandler(be, key) ch, err := restic.NewContentHandler(be, key)

View File

@ -1,13 +1,11 @@
package main package main
import ( import (
"errors"
"fmt" "fmt"
"log"
"net/url" "net/url"
"os" "os"
"runtime" "runtime"
"sort"
"strings"
"golang.org/x/crypto/ssh/terminal" "golang.org/x/crypto/ssh/terminal"
@ -22,6 +20,8 @@ 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"`
} }
var parser = flags.NewParser(&opts, flags.Default)
func errx(code int, format string, data ...interface{}) { func errx(code int, format string, data ...interface{}) {
if len(format) > 0 && format[len(format)-1] != '\n' { if len(format) > 0 && format[len(format)-1] != '\n' {
format += "\n" format += "\n"
@ -30,10 +30,6 @@ func errx(code int, format string, data ...interface{}) {
os.Exit(code) os.Exit(code)
} }
type commandFunc func(backend.Server, *restic.Key, []string) error
var commands = make(map[string]commandFunc)
func readPassword(env string, prompt string) string { func readPassword(env string, prompt string) string {
if env != "" { if env != "" {
@ -54,7 +50,13 @@ func readPassword(env string, prompt string) string {
return string(pw) return string(pw)
} }
func commandInit(repo string) error { type CmdInit struct{}
func (cmd CmdInit) Execute(args []string) error {
if opts.Repo == "" {
return errors.New("Please specify repository location (-r)")
}
pw := readPassword("RESTIC_PASSWORD", "enter password for new backend: ") pw := readPassword("RESTIC_PASSWORD", "enter password for new backend: ")
pw2 := readPassword("RESTIC_PASSWORD", "enter password again: ") pw2 := readPassword("RESTIC_PASSWORD", "enter password again: ")
@ -62,15 +64,15 @@ func commandInit(repo string) error {
errx(1, "passwords do not match") errx(1, "passwords do not match")
} }
be, err := create(repo) be, err := create(opts.Repo)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "creating backend at %s failed: %v\n", repo, err) fmt.Fprintf(os.Stderr, "creating backend at %s failed: %v\n", opts.Repo, err)
os.Exit(1) os.Exit(1)
} }
_, err = restic.CreateKey(be, pw) _, err = restic.CreateKey(be, pw)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "creating key in backend at %s failed: %v\n", repo, err) fmt.Fprintf(os.Stderr, "creating key in backend at %s failed: %v\n", opts.Repo, err)
os.Exit(1) os.Exit(1)
} }
@ -125,73 +127,99 @@ func create(u string) (backend.Server, error) {
return backend.CreateSFTP(url.Path[1:], "ssh", args...) return backend.CreateSFTP(url.Path[1:], "ssh", args...)
} }
func OpenRepo() (backend.Server, *restic.Key, error) {
be, err := open(opts.Repo)
if err != nil {
return nil, nil, err
}
key, err := restic.SearchKey(be, readPassword("RESTIC_PASSWORD", "Enter Password for Repository: "))
if err != nil {
return nil, nil, fmt.Errorf("unable to open repo: %v", err)
}
return be, key, nil
}
func init() { func init() {
// set GOMAXPROCS to number of CPUs // set GOMAXPROCS to number of CPUs
runtime.GOMAXPROCS(runtime.NumCPU()) runtime.GOMAXPROCS(runtime.NumCPU())
_, err := parser.AddCommand("init",
"create repository",
"The init command creates a new repository",
&CmdInit{})
if err != nil {
panic(err)
}
} }
func main() { func main() {
// defer profile.Start(profile.MemProfileRate(100000), profile.ProfilePath(".")).Stop() // defer profile.Start(profile.MemProfileRate(100000), profile.ProfilePath(".")).Stop()
log.SetOutput(os.Stdout)
opts.Repo = os.Getenv("RESTIC_REPOSITORY") opts.Repo = os.Getenv("RESTIC_REPOSITORY")
args, err := flags.Parse(&opts) _, err := parser.Parse()
if e, ok := err.(*flags.Error); ok && e.Type == flags.ErrHelp { if e, ok := err.(*flags.Error); ok && e.Type == flags.ErrHelp {
os.Exit(0) os.Exit(0)
} }
if opts.Repo == "" { if err != nil {
fmt.Fprintf(os.Stderr, "no repository specified, use -r or RESTIC_REPOSITORY variable\n")
os.Exit(1) os.Exit(1)
} }
if len(args) == 0 { // fmt.Printf("parser: %#v\n", parser)
cmds := []string{"init"} // fmt.Printf("%#v\n", parser.Active.Name)
for k := range commands {
cmds = append(cmds, k)
}
sort.Strings(cmds)
fmt.Printf("nothing to do, available commands: [%v]\n", strings.Join(cmds, "|"))
os.Exit(0)
}
cmd := args[0] // if opts.Repo == "" {
// fmt.Fprintf(os.Stderr, "no repository specified, use -r or RESTIC_REPOSITORY variable\n")
// os.Exit(1)
// }
switch cmd { // if len(args) == 0 {
case "init": // cmds := []string{"init"}
err = commandInit(opts.Repo) // for k := range commands {
if err != nil { // cmds = append(cmds, k)
errx(1, "error executing command %q: %v", cmd, err) // }
} // sort.Strings(cmds)
return // fmt.Printf("nothing to do, available commands: [%v]\n", strings.Join(cmds, "|"))
// os.Exit(0)
// }
case "version": // cmd := args[0]
fmt.Printf("%v\n", version)
return
}
f, ok := commands[cmd] // switch cmd {
if !ok { // case "init":
errx(1, "unknown command: %q\n", cmd) // err = commandInit(opts.Repo)
} // if err != nil {
// errx(1, "error executing command %q: %v", cmd, err)
// }
// return
// read_password("enter password: ") // case "version":
repo, err := open(opts.Repo) // fmt.Printf("%v\n", version)
if err != nil { // return
errx(1, "unable to open repo: %v", err) // }
}
key, err := restic.SearchKey(repo, readPassword("RESTIC_PASSWORD", "Enter Password for Repository: ")) // f, ok := commands[cmd]
if err != nil { // if !ok {
errx(2, "unable to open repo: %v", err) // errx(1, "unknown command: %q\n", cmd)
} // }
err = f(repo, key, args[1:]) // // read_password("enter password: ")
if err != nil { // repo, err := open(opts.Repo)
errx(1, "error executing command %q: %v", cmd, err) // if err != nil {
} // errx(1, "unable to open repo: %v", err)
// }
restic.PoolAlloc() // key, err := restic.SearchKey(repo, readPassword("RESTIC_PASSWORD", "Enter Password for Repository: "))
// if err != nil {
// errx(2, "unable to open repo: %v", err)
// }
// err = f(repo, key, args[1:])
// if err != nil {
// errx(1, "error executing command %q: %v", cmd, err)
// }
// restic.PoolAlloc()
} }