Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 62 additions & 16 deletions backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/docker/go-connections/nat"
"github.com/kennygrant/sanitize"
"github.com/spf13/cobra"
"github.com/zloylos/grsync"
)

// Backup is used to gather all of a container's metadata, so we can encode it
Expand All @@ -31,6 +32,7 @@ type Backup struct {
var (
optLaunch = ""
optTar = false
optOut = false
optAll = false
optStopped = false

Expand Down Expand Up @@ -178,29 +180,72 @@ func backup(ID string) error {
return err
}

for _, m := range conf.Mounts {
// fmt.Printf("Mount (type %s) %s -> %s\n", m.Type, m.Source, m.Destination)
err := filepath.Walk(m.Source, collectFile)
var backupFiles = conf.Mounts

if optOut {
outDir := filename + ".backup"
for _, m := range backupFiles {
source := m.Source
dest := outDir + source
if info, err := os.Stat(source); err == nil && info.IsDir() {
source += "/"
dest += "/"
}
fmt.Println("Backing up '" + source + "' -> './" + dest + "'")
task := grsync.NewTask(
source,
dest,
grsync.RsyncOptions{},
)

go func() {
for {
state := task.State()
fmt.Printf(
"\rprogress: %.2f / rem. %d / tot. %d",
state.Progress,
state.Remain,
state.Total,
)
time.Sleep(time.Second)
}
}()

err = task.Run()
if err != nil {
fmt.Println(task.Log().Stderr)
return err
}
fmt.Printf("\r")
}

} else {

paths = []string{}
for _, m := range backupFiles {
// fmt.Printf("Mount (type %s) %s -> %s\n", m.Type, m.Source, m.Destination)
err := filepath.Walk(m.Source, collectFile)
if err != nil {
return err
}
}

filelist, err := os.Create(filename + ".backup.files")
if err != nil {
return err
}
}

filelist, err := os.Create(filename + ".backup.files")
if err != nil {
return err
}
defer filelist.Close()
defer filelist.Close()

_, err = filelist.WriteString(filename + ".backup.json\n")
if err != nil {
return err
}
for _, s := range paths {
_, err := filelist.WriteString(s + "\n")
_, err = filelist.WriteString(filename + ".backup.json\n")
if err != nil {
return err
}
for _, s := range paths {
_, err := filelist.WriteString(s + "\n")
if err != nil {
return err
}
}
}

fmt.Println("Created backup:", filename+".backup.json")
Expand Down Expand Up @@ -241,6 +286,7 @@ func backupAll() error {
func init() {
backupCmd.Flags().StringVarP(&optLaunch, "launch", "l", "", "launch external program with file-list as argument")
backupCmd.Flags().BoolVarP(&optTar, "tar", "t", false, "create tar backups")
backupCmd.Flags().BoolVarP(&optOut, "outdir", "o", false, "copy all files to folder")
backupCmd.Flags().BoolVarP(&optAll, "all", "a", false, "backup all running containers")
backupCmd.Flags().BoolVarP(&optStopped, "stopped", "s", false, "in combination with --all: also backup stopped containers")
RootCmd.AddCommand(backupCmd)
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ require (
github.com/spf13/cobra v0.0.3
github.com/spf13/pflag v1.0.3 // indirect
golang.org/x/net v0.0.0-20190502183928-7f726cade0ab // indirect
github.com/zloylos/grsync v1.3.0 // indirect
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect
google.golang.org/grpc v1.20.1 // indirect
gotest.tools v2.2.0+incompatible // indirect
Expand Down
76 changes: 76 additions & 0 deletions restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ import (
"io/ioutil"
"os"
"strings"
"time"

"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/spf13/cobra"
"github.com/zloylos/grsync"
)

var (
Expand Down Expand Up @@ -158,6 +160,11 @@ func restore(filename string) error {
return err
}

err = restoreFiles(filename, id, backup)
if err != nil {
return err
}

if optStart {
return startContainer(id)
}
Expand Down Expand Up @@ -196,6 +203,74 @@ func createContainer(backup Backup) (string, error) {
return resp.ID, nil
}

func restoreFiles(filename string, id string, backup Backup) error {
conf, err := cli.ContainerInspect(ctx, id)
if err != nil {
return err
}

tt := map[string]string{}
for _, oldPath := range backup.Mounts {
for _, hostPath := range conf.Mounts {
if oldPath.Destination == hostPath.Destination {
tt[oldPath.Source] = hostPath.Source
break
}
}
}

outDir := strings.TrimRight(filename, ".json")
for oldPath, newPath := range tt {
skip := false
for _, exclude := range optExclude {
if strings.HasPrefix(oldPath, exclude) {
skip = true
break
}
}

if skip {
fmt.Println("Skipping './" + backupFiles + "'")
continue
}

backupFiles := outDir + oldPath
fmt.Println("Restoring './" + backupFiles + "' -> '" + newPath + "'")

if info, err := os.Stat(backupFiles); err == nil && info.IsDir() {
backupFiles += "/"
newPath += "/"
}

task := grsync.NewTask(
backupFiles,
newPath,
grsync.RsyncOptions{},
)

go func() {
for {
state := task.State()
fmt.Printf(
"\rprogress: %.2f / rem. %d / tot. %d",
state.Progress,
state.Remain,
state.Total,
)
time.Sleep(time.Second)
}
}()

err = task.Run()
if err != nil {
return err
}
fmt.Printf("\r")
}

return nil
}

func startContainer(id string) error {
fmt.Println("Starting container:", id[:12])

Expand Down Expand Up @@ -226,5 +301,6 @@ func startContainer(id string) error {

func init() {
restoreCmd.Flags().BoolVarP(&optStart, "start", "s", false, "start restored container")
restoreCmd.Flags().StringArrayVarP(&optExclude, "exclude", "e", []string{}, "skip restoring files that start with this, can use multiple times")
RootCmd.AddCommand(restoreCmd)
}