-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add support for zfs datasets and zvols
- Loading branch information
1 parent
f47e132
commit 7e49b91
Showing
12 changed files
with
450 additions
and
55 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,18 @@ | ||
# restic-plus | ||
|
||
This is a simple wrapper around the normal [Restic](https://github.com/restic/restic) binary. | ||
|
||
## Additions | ||
|
||
* YAML based configuration file, see [here](restic-plus.yaml.example). | ||
* ZFS datasets and ZFS zvols support while leveraging ZFS snapshots to garuantuee consistent backups. | ||
|
||
## Restrictions | ||
|
||
* Currently is opinionated and only works with SFTP. | ||
|
||
## Usage | ||
|
||
* `restic-plus backup`: Run backups | ||
* `restic-plus cron`: Run backups and do cleanup afterwards | ||
* `restic-plus -- xxx`: Forward to original restic binary |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package cmd | ||
|
||
import ( | ||
"strconv" | ||
|
||
"github.com/choffmeister/restic-plus/internal" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
var ( | ||
cleanupCmd = &cobra.Command{ | ||
Use: "cleanup", | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
internal.LogInfo.Printf("Cleaning up...\n") | ||
config := rootContext.Config | ||
cleanup := config.Cron.Cleanup | ||
if cleanup.Enabled { | ||
args := []string{"forget", "--prune"} | ||
if cleanup.Keep.Last > 0 { | ||
args = append(args, "--keep-last", strconv.Itoa(cleanup.Keep.Last)) | ||
} | ||
if cleanup.Keep.Daily > 0 { | ||
args = append(args, "--keep-daily", strconv.Itoa(cleanup.Keep.Daily)) | ||
} | ||
if cleanup.Keep.Weekly > 0 { | ||
args = append(args, "--keep-weekly", strconv.Itoa(cleanup.Keep.Weekly)) | ||
} | ||
if cleanup.Keep.Monthly > 0 { | ||
args = append(args, "--keep-monthly", strconv.Itoa(cleanup.Keep.Monthly)) | ||
} | ||
if cleanup.Keep.Yearly > 0 { | ||
args = append(args, "--keep-yearly", strconv.Itoa(cleanup.Keep.Yearly)) | ||
} | ||
|
||
if err := internal.ExecRestic(rootContext, args...); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
}, | ||
} | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package internal | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"io" | ||
"log" | ||
"os" | ||
"os/exec" | ||
"time" | ||
) | ||
|
||
type ExecCommandOpts struct { | ||
Name string | ||
Args []string | ||
Env []string | ||
Logger *log.Logger | ||
} | ||
|
||
func ExecCommand(name string, args ...string) (string, int, error) { | ||
return ExecCommandWithOpts(ExecCommandOpts{ | ||
Name: name, | ||
Args: args, | ||
}) | ||
} | ||
|
||
func ExecCommandWithOpts(opts ExecCommandOpts) (string, int, error) { | ||
logger := opts.Logger | ||
if logger == nil { | ||
logger = LogDebug | ||
} | ||
|
||
LogDebug.Printf("Executing command %s %v\n", opts.Name, opts.Args) | ||
cmd := exec.Command(opts.Name, opts.Args...) | ||
|
||
var outputBuffer bytes.Buffer | ||
logWriter := NewLogWriter(logger) | ||
writer := io.MultiWriter(&outputBuffer, logWriter) | ||
cmd.Stdout = writer | ||
cmd.Stderr = writer | ||
cmd.Env = append(os.Environ(), opts.Env...) | ||
|
||
err := cmd.Run() | ||
outputStr := outputBuffer.String() | ||
if err != nil { | ||
exitError, ok := err.(*exec.ExitError) | ||
if !ok { | ||
return outputStr, 0, err | ||
} | ||
return outputStr, exitError.ExitCode(), fmt.Errorf("%w\n%s", exitError, outputStr) | ||
} | ||
return outputStr, 0, nil | ||
} | ||
|
||
func ExecCommandRetry(name string, args ...string) (string, int, error) { | ||
return ExecCommandRetryWithOpts(ExecCommandOpts{ | ||
Name: name, | ||
Args: args, | ||
}) | ||
} | ||
|
||
func ExecCommandRetryWithOpts(opts ExecCommandOpts) (string, int, error) { | ||
maxAttempts := 10 | ||
delay := 1000 * time.Millisecond | ||
lastOutput := "" | ||
lastCode := 0 | ||
lastErr := (error)(nil) | ||
attempt := 0 | ||
for attempt < maxAttempts { | ||
if output, code, err := ExecCommandWithOpts(opts); err == nil { | ||
return output, code, err | ||
} else { | ||
lastOutput = output | ||
lastCode = code | ||
lastErr = err | ||
} | ||
attempt = attempt + 1 | ||
time.Sleep(delay) | ||
} | ||
return lastOutput, lastCode, lastErr | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,47 @@ | ||
package internal | ||
|
||
import ( | ||
"bytes" | ||
"log" | ||
"os" | ||
"sync" | ||
) | ||
|
||
var ( | ||
Debug = log.New(os.Stderr, "DEBUG: ", log.Ltime|log.Lshortfile) | ||
Warn = log.New(os.Stderr, "WARN: ", log.Ltime|log.Lshortfile) | ||
Info = log.New(os.Stderr, "INFO: ", log.Ltime|log.Lshortfile) | ||
Error = log.New(os.Stderr, "ERROR: ", log.Ltime|log.Lshortfile) | ||
LogDebug = log.New(os.Stdout, "DEBUG: ", 0) | ||
LogInfo = log.New(os.Stdout, "INFO: ", 0) | ||
LogWarn = log.New(os.Stdout, "WARN: ", 0) | ||
LogError = log.New(os.Stdout, "ERROR: ", log.Lshortfile) | ||
LogRestic = log.New(os.Stdout, "RESTIC: ", 0) | ||
) | ||
|
||
type LogWriter struct { | ||
mu sync.Mutex | ||
logger *log.Logger | ||
buffer []byte | ||
} | ||
|
||
func NewLogWriter(logger *log.Logger) *LogWriter { | ||
return &LogWriter{logger: logger} | ||
} | ||
|
||
func (l *LogWriter) Write(p []byte) (n int, err error) { | ||
newline := byte('\n') | ||
breakline := []byte("\r") | ||
empty := []byte("") | ||
|
||
l.mu.Lock() | ||
defer l.mu.Unlock() | ||
|
||
l.buffer = append(l.buffer, bytes.ReplaceAll(p, breakline, empty)...) | ||
|
||
newlineIndex := bytes.IndexByte(l.buffer, newline) | ||
for newlineIndex >= 0 { | ||
line := l.buffer[0:newlineIndex] | ||
l.logger.Printf("%s\n", string(line)) | ||
l.buffer = l.buffer[newlineIndex+1:] | ||
newlineIndex = bytes.IndexByte(l.buffer, newline) | ||
} | ||
|
||
return len(p), nil | ||
} |
Oops, something went wrong.