Commit 9b40732a authored by ale's avatar ale

Parse restic --json output to obtain snapshot ID

parent 6447e19b
......@@ -172,7 +172,7 @@ func (r *resticRepository) cacheArgs(ds *Dataset) string {
func (r *resticRepository) BackupCmd(backup *Backup, ds *Dataset, inputFile string) string {
return fmt.Sprintf(
"%s backup %s --exclude-caches --one-file-system %s --files-from %s",
"%s backup %s --json --exclude-caches --one-file-system %s --files-from %s",
r.resticCmd(),
r.cacheArgs(ds),
resticBackupTags(backup, ds),
......@@ -245,7 +245,7 @@ func (r *resticRepository) BackupStreamCmd(backup *Backup, ds *Dataset) string {
fakePath := datasetStdinPath(ds)
return fmt.Sprintf(
// The --force prevents restic from trying to find a previous snapshot.
"%s backup %s %s --force --stdin --stdin-filename %s",
"%s backup %s %s --json --force --stdin --stdin-filename %s",
r.resticCmd(),
r.cacheArgs(ds),
resticBackupTags(backup, ds),
......@@ -272,14 +272,46 @@ func (r *resticRepository) RestoreStreamCmd(ctx context.Context, rctx RuntimeCon
), nil
}
var resticSnapshotRx = regexp.MustCompile(`^snapshot ([0-9a-f]{8}) saved`)
// Generic JSON message from 'restic backup'.
type resticMessage struct {
MessageType string `json:"message_type"`
// Status.
SecondsElapsed int64 `json:"seconds_elapsed"`
PercentDone float64 `json:"percent_done"`
TotalFiles int64 `json:"total_files"`
FilesDone int64 `json:"files_done"`
TotalBytes int64 `json:"total_bytes"`
BytesDone int64 `json:"bytes_done"`
CurrentFiles []string `json:"current_files"`
// Summary.
FilesNew int64 `json:"files_new"`
FilesChanged int64 `json:"files_changed"`
FilesUnmodified int64 `json:"files_unmodified"`
DirsNew int64 `json:"dirs_new"`
DirsChanged int64 `json:"dirs_changed"`
DirsUnmodified int64 `json:"dirs_unmodified"`
DataBlobs int64 `json:"data_blobs"`
TreeBlobs int64 `json:"tree_blobs"`
DataAdded int64 `json:"data_added"`
TotalFilesProcessed int64 `json:"total_files_processed"`
TotalBytesProcessed int64 `json:"total_bytes_processed"`
TotalDuration float64 `json:"total_duration"`
SnapshotID string `json:"snapshot_id"`
}
// Scan the output of 'restic backup' for the snapshot ID. Modifies backup/dataset objects.
func (r *resticRepository) RunBackup(ctx context.Context, shell *Shell, backup *Backup, ds *Dataset, cmd string) error {
return shell.RunWithStdoutCallback(ctx, cmd, func(line string) {
m := resticSnapshotRx.FindStringSubmatch(line)
if len(m) > 1 && m[1] != "" {
ds.SnapshotID = m[1]
return shell.RunWithStdoutCallback(ctx, cmd, func(line []byte) {
var msg resticMessage
if err := json.Unmarshal(line, &msg); err != nil {
log.Printf("error parsing restic JSON message: %v (%s)", err, line)
return
}
if msg.MessageType == "summary" {
log.Printf("dataset %s: total_bytes=%d, new=%d", ds.ID, msg.TotalBytesProcessed, msg.DataAdded)
ds.SnapshotID = msg.SnapshotID
}
})
}
......
......@@ -78,7 +78,7 @@ func (s *Shell) SetIOClass(n int) {
func (s *Shell) command(ctx context.Context, arg string) *exec.Cmd {
var args []string
if s.dryRun {
args = []string{"/bin/echo", arg}
args = []string{"/bin/true", arg}
} else {
// The pipefail option is necessary for us to detect
// when the first command in a pipeline fails, but we
......@@ -117,7 +117,7 @@ func (s *Shell) command(ctx context.Context, arg string) *exec.Cmd {
// RunWithStdoutCallback executes a command and invokes a callback on
// every line read from its standard output. Stdandard output and
// error are still logged normally as in Run().
func (s *Shell) RunWithStdoutCallback(ctx context.Context, arg string, stdoutCallback func(string)) error {
func (s *Shell) RunWithStdoutCallback(ctx context.Context, arg string, stdoutCallback func([]byte)) error {
c := s.command(ctx, arg)
stdout, err := c.StdoutPipe()
......@@ -140,10 +140,10 @@ func (s *Shell) RunWithStdoutCallback(ctx context.Context, arg string, stdoutCal
go func() {
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
line := scanner.Text()
log.Printf("%s%s", logPrefix, line)
if stdoutCallback != nil {
stdoutCallback(line)
stdoutCallback(scanner.Bytes())
} else {
log.Printf("%s%s", logPrefix, scanner.Bytes())
}
}
}()
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment