-
Notifications
You must be signed in to change notification settings - Fork 0
/
create_backup.go
85 lines (70 loc) · 3.32 KB
/
create_backup.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package main
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/alessio/shellescape"
)
// CreateBackup runs all necessary commands to create a new backup based on the passed
// backup name thisBackupName and the relative path lastBackupRelativePath to the last backup
// to use as hard link destination. Note that lastBackupRelativePath is relative to the MAIn
// target folder
func CreateBackup(options *Options, thisBackupName string, lastBackupRelativePath string) {
Log.Info.Printf("Backing up sources: %v", options.sources)
// Add target, check for existence and create if necessary
// Use a temporary folder and rename to an error folder if anything fails. That way, if the script is interrupted
// or ends in an error, the temporary/error folders won't crowd out the actual folders during groups/excess deletes.
targetPath := NormalizeFolderPath(filepath.Join(options.TargetPath(), thisBackupName))
progressTargetPath := NormalizeFolderPath(filepath.Join(options.TargetPath(), thisBackupName+"_progress"))
errorTargetPath := NormalizeFolderPath(filepath.Join(options.TargetPath(), thisBackupName+"_error"))
args := []string{"-a", "--delete"}
if lastBackupRelativePath != "" {
// --link-dest must be relative to the TARGET FOLDER, which means the NEWLY created backup folder
// (not the "main" folder). Hence the "../".
// It does not take user:host@ before the relative path, but figures that out itself
args = append(args, "--link-dest", NormalizeFolderPath(filepath.Join("../", lastBackupRelativePath)))
}
args = append(args, options.rsyncOptions...)
args = append(args, "-e", fmt.Sprintf("ssh %s", strings.Join(options.SSHOptions(), " ")))
for _, source := range options.sources {
args = append(args, source)
}
if options.IsRemoteTarget() {
args = append(args, fmt.Sprintf("%s:%s", options.targetHost, progressTargetPath))
} else {
args = append(args, progressTargetPath)
}
Log.Debug.Printf("createBackup: cmdLine: rsync %s", strings.Join(args, " "))
// _, _, _, err := call("printenv", []string{})
_, _, exitCode, err := call("rsync", args, "rsync", Log.Info)
if err != nil {
if exitCode == 23 || exitCode == 24 || exitCode == 25 {
Log.Warn.Printf("Rsync exited with exit code %v; indicating that some files could not be transfered/deleted.", exitCode)
} else {
Log.Fatal.Printf("Error executing rsync command: %v", err)
Log.Debug.Printf("Renaming progress folder %s to %s", progressTargetPath, errorTargetPath)
mvErr := os.Rename(progressTargetPath, errorTargetPath)
if mvErr != nil {
Log.Fatal.Printf("Could not rename progress folder %s to error folder %s", progressTargetPath, errorTargetPath)
}
panic(fmt.Sprintf("Error executing rsync command: %v", err))
}
}
Log.Debug.Printf("Renaming temporary folder %s to %s", progressTargetPath, targetPath)
if options.IsRemoteTarget() {
_, _, _, err := sshCall(
options,
fmt.Sprintf("mv %s %s", shellescape.Quote(progressTargetPath), shellescape.Quote(targetPath)),
Log.Debug,
)
if err != nil {
panic(fmt.Sprintf("Could not rename remote progress folder %s to final target folder %s", progressTargetPath, targetPath))
}
} else {
mvErr := os.Rename(progressTargetPath, targetPath)
if mvErr != nil {
panic(fmt.Sprintf("Could not rename progress folder %s to final target folder %s", progressTargetPath, targetPath))
}
}
}