add unit test and make importable
parent
fbb36bc94a
commit
b27376a8b9
164
main.go
164
main.go
|
|
@ -1,59 +1,34 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"local/args"
|
"local/args"
|
||||||
"local/storage"
|
"local/lastn/lastn"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
n int
|
|
||||||
conf string
|
|
||||||
rclone string
|
|
||||||
ns string
|
|
||||||
root string
|
|
||||||
store string
|
|
||||||
cmd string
|
|
||||||
}
|
|
||||||
|
|
||||||
type LastN struct {
|
|
||||||
store storage.DB
|
|
||||||
conf Config
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
conf := config()
|
conf := config()
|
||||||
log.Println(conf)
|
log.Println(conf)
|
||||||
storage, err := storage.New(storage.TypeFromString(conf.store), conf.conf, path.Join(conf.rclone+":", conf.ns))
|
lastn, err := lastn.New(conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
lastn := &LastN{
|
|
||||||
conf: conf,
|
|
||||||
store: storage,
|
|
||||||
}
|
|
||||||
actions := []func() error{}
|
actions := []func() error{}
|
||||||
switch conf.cmd {
|
switch conf.Cmd {
|
||||||
case "backup":
|
case "backup":
|
||||||
actions = append(actions, lastn.push, lastn.clean)
|
actions = append(actions, lastn.Push, lastn.Clean)
|
||||||
case "list":
|
case "list":
|
||||||
actions = append(actions, lastn.list)
|
actions = append(actions, lastn.List)
|
||||||
case "clean":
|
case "clean":
|
||||||
actions = append(actions, lastn.clean)
|
actions = append(actions, lastn.Clean)
|
||||||
case "restore":
|
case "restore":
|
||||||
actions = append(actions, lastn.restore)
|
actions = append(actions, lastn.Restore)
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("not impl: %s"))
|
panic(fmt.Sprintf("not impl: %s", conf.Cmd))
|
||||||
}
|
}
|
||||||
for _, action := range actions {
|
for _, action := range actions {
|
||||||
if err := action(); err != nil {
|
if err := action(); err != nil {
|
||||||
|
|
@ -62,7 +37,7 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func config() Config {
|
func config() lastn.Config {
|
||||||
as := args.NewArgSet()
|
as := args.NewArgSet()
|
||||||
as.Append(args.INT, "n", "number of backups to retain", 5)
|
as.Append(args.INT, "n", "number of backups to retain", 5)
|
||||||
as.Append(args.STRING, "conf", "path to rclone conf", path.Join(os.Getenv("HOME"), "/.config/rclone/rclone.conf"))
|
as.Append(args.STRING, "conf", "path to rclone conf", path.Join(os.Getenv("HOME"), "/.config/rclone/rclone.conf"))
|
||||||
|
|
@ -78,118 +53,13 @@ func config() Config {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
return Config{
|
return lastn.Config{
|
||||||
n: as.Get("n").GetInt(),
|
N: as.Get("n").GetInt(),
|
||||||
conf: as.Get("conf").GetString(),
|
Conf: as.Get("conf").GetString(),
|
||||||
rclone: as.Get("rclone").GetString(),
|
Rclone: as.Get("rclone").GetString(),
|
||||||
root: root,
|
Root: root,
|
||||||
ns: as.Get("ns").GetString(),
|
Ns: as.Get("ns").GetString(),
|
||||||
store: as.Get("store").GetString(),
|
Store: as.Get("store").GetString(),
|
||||||
cmd: as.Get("cmd").GetString(),
|
Cmd: as.Get("cmd").GetString(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lastN *LastN) push() error {
|
|
||||||
root := lastN.conf.root
|
|
||||||
store := lastN.store
|
|
||||||
root, err := filepath.Abs(root)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
archive := path.Join(
|
|
||||||
os.TempDir(),
|
|
||||||
fmt.Sprintf(
|
|
||||||
"%s.tar",
|
|
||||||
time.Now().Format("2006.01.02.15.04.05"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
cmd := exec.Command(
|
|
||||||
"tar",
|
|
||||||
"-czf",
|
|
||||||
archive,
|
|
||||||
"-C",
|
|
||||||
path.Dir(root),
|
|
||||||
path.Base(root),
|
|
||||||
)
|
|
||||||
out, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("%v: %s", err, out)
|
|
||||||
}
|
|
||||||
b, err := ioutil.ReadFile(archive)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Println("Created backup", path.Base(archive))
|
|
||||||
return store.Set(path.Base(archive), b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lastN *LastN) clean() error {
|
|
||||||
n := lastN.conf.n
|
|
||||||
store := lastN.store
|
|
||||||
backups, err := store.List(nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
sort.Strings(backups)
|
|
||||||
for i := 0; i < len(backups)-n; i++ {
|
|
||||||
log.Println("Pruning old backup", backups[i])
|
|
||||||
err := store.Set(backups[i], nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lastN *LastN) list() error {
|
|
||||||
store := lastN.store
|
|
||||||
backups, err := store.List(nil)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
sort.Strings(backups)
|
|
||||||
for _, backup := range backups {
|
|
||||||
log.Println(backup)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (lastN *LastN) restore() error {
|
|
||||||
root := lastN.conf.root + "-restore"
|
|
||||||
os.RemoveAll(root)
|
|
||||||
if err := os.MkdirAll(root, os.ModePerm); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
store := lastN.store
|
|
||||||
backups, err := store.List(nil)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot list: %v", err)
|
|
||||||
}
|
|
||||||
sort.Strings(backups)
|
|
||||||
backup := backups[len(backups)-1]
|
|
||||||
b, err := store.Get(backup)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot get %s: %v", backup, err)
|
|
||||||
}
|
|
||||||
log.Printf("restoring %s (%v) in %s", backup, len(b), root)
|
|
||||||
cmd := exec.Command(
|
|
||||||
"tar",
|
|
||||||
"-C",
|
|
||||||
root,
|
|
||||||
"-xzf",
|
|
||||||
"-",
|
|
||||||
)
|
|
||||||
stdin, err := cmd.StdinPipe()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot get stdin: %v", err)
|
|
||||||
}
|
|
||||||
go func() {
|
|
||||||
defer stdin.Close()
|
|
||||||
io.Copy(stdin, bytes.NewReader(b))
|
|
||||||
}()
|
|
||||||
out, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed tar -xf: %v: %s", err, out)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue